mirror of https://github.com/micromata/borgbackup-butler.git

Kai Reinhard
14.54.2018 41daf2acb8954a168a3ef370ba9633a41b51ac11
Renamed Borg json objects. Started with own objects, such as Repository.
4 files added
11 files renamed
9 files modified
442 ■■■■ changed files
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java 31 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ArchiveFilelistCache.java 28 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java 59 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/data/Repository.java 50 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgArchive.java 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgArchive2.java 4 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgArchiveInfo.java 10 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgArchiveStats.java 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgCache.java 4 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgEncryption.java 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgFilesystemItem.java 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgRepoInfo.java 8 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgRepoList.java 8 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgRepository.java 7 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgStats.java 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/utils/DateUtils.java 15 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/test/java/de/micromata/borgbutler/cache/ArchiveFilelistCacheTest.java 36 ●●●● patch | view | raw | blame | history
borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java 26 ●●●● patch | view | raw | blame | history
borgbutler-core/src/test/java/de/micromata/borgbutler/utils/DateUtilsTest.java 25 ●●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/Main.java 6 ●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ReposRest.java 7 ●●●●● patch | view | raw | blame | history
borgbutler-webapp/src/components/views/repos/RepoArchiveList.jsx 104 ●●●●● patch | view | raw | blame | history
borgbutler-webapp/src/components/views/repos/RepoCard.jsx 2 ●●● patch | view | raw | blame | history
borgbutler-webapp/src/components/views/repos/RepoListView.jsx 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java
@@ -4,8 +4,10 @@
import de.micromata.borgbutler.config.Configuration;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.config.Definitions;
import de.micromata.borgbutler.data.Repository;
import de.micromata.borgbutler.json.JsonUtils;
import de.micromata.borgbutler.json.borg.*;
import de.micromata.borgbutler.utils.DateUtils;
import org.apache.commons.exec.*;
import org.apache.commons.exec.environment.EnvironmentUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
@@ -29,22 +31,31 @@
     * @param repoConfig
     * @return Parsed repo config returned by Borg command.
     */
    public static RepoInfo info(BorgRepoConfig repoConfig) {
    public static Repository info(BorgRepoConfig repoConfig) {
        String json = execute(repoConfig, "info", repoConfig.getRepo(), "--json");
        if (json == null) {
            return null;
        }
        RepoInfo repoInfo = JsonUtils.fromJson(RepoInfo.class, json);
        BorgRepoInfo repoInfo = JsonUtils.fromJson(BorgRepoInfo.class, json);
        repoInfo.setOriginalJson(json);
        return repoInfo;
        Repository repository = new Repository();
        BorgRepository borgRepository = repoInfo.getRepository();
        repository.setId(borgRepository.getId())
                .setLastModified(DateUtils.get(borgRepository.getLastModified()))
                .setLocation(borgRepository.getLocation())
                .setName(borgRepository.getName())
                .setCache(repoInfo.getCache())
                .setEncryption(repoInfo.getEncryption())
                .setSecurityDir(repoInfo.getSecurityDir());
        return repository;
    }
    public static RepoList list(BorgRepoConfig repoConfig) {
    public static BorgRepoList list(BorgRepoConfig repoConfig) {
        String json = execute(repoConfig, "list", repoConfig.getRepo(), "--json");
        if (json == null) {
            return null;
        }
        RepoList repoList = JsonUtils.fromJson(RepoList.class, json);
        BorgRepoList repoList = JsonUtils.fromJson(BorgRepoList.class, json);
        repoList.setOriginalJson(json);
        return repoList;
    }
@@ -56,26 +67,26 @@
     * @param archive
     * @return
     */
    public static ArchiveInfo info(BorgRepoConfig repoConfig, String archive) {
    public static BorgArchiveInfo info(BorgRepoConfig repoConfig, String archive) {
        String json = execute(repoConfig, "info", repoConfig.getRepo() + "::" + archive, "--json");
        if (json == null) {
            return null;
        }
        ArchiveInfo archiveInfo = JsonUtils.fromJson(ArchiveInfo.class, json);
        BorgArchiveInfo archiveInfo = JsonUtils.fromJson(BorgArchiveInfo.class, json);
        archiveInfo.setOriginalJson(json);
        return archiveInfo;
    }
    public static List<FilesystemItem> listArchiveContent(BorgRepoConfig repoConfig, Archive archive) {
    public static List<BorgFilesystemItem> listArchiveContent(BorgRepoConfig repoConfig, BorgArchive archive) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        execute(outputStream, repoConfig, "list", repoConfig.getRepo() + "::" + archive.getArchive(),
                "--json-lines");
        String response = outputStream.toString(Definitions.STD_CHARSET);
        List<FilesystemItem> content = new ArrayList<>();
        List<BorgFilesystemItem> content = new ArrayList<>();
        try (Scanner scanner = new Scanner(response)) {
            while (scanner.hasNextLine()) {
                String json = scanner.nextLine();
                FilesystemItem item = JsonUtils.fromJson(FilesystemItem.class, json);
                BorgFilesystemItem item = JsonUtils.fromJson(BorgFilesystemItem.class, json);
                content.add(item);
            }
        }
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ArchiveFilelistCache.java
@@ -1,8 +1,8 @@
package de.micromata.borgbutler.cache;
import de.micromata.borgbutler.config.BorgRepoConfig;
import de.micromata.borgbutler.json.borg.Archive;
import de.micromata.borgbutler.json.borg.FilesystemItem;
import de.micromata.borgbutler.json.borg.BorgArchive;
import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
import de.micromata.borgbutler.utils.ReplaceUtils;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
@@ -29,10 +29,10 @@
    private long FILES_EXPIRE_TIME = 7 * 24 * 3660 * 1000; // Expires after 7 days.
    @Getter
    private Archive archive;
    private List<FilesystemItem> content;
    private BorgArchive archive;
    private List<BorgFilesystemItem> content;
    public void save(BorgRepoConfig repoConfig, Archive archive, List<FilesystemItem> filesystemItems) {
    public void save(BorgRepoConfig repoConfig, BorgArchive archive, List<BorgFilesystemItem> filesystemItems) {
        File file = getFile(repoConfig, archive);
        if (CollectionUtils.isEmpty(filesystemItems)) {
            return;
@@ -40,7 +40,7 @@
        log.info("Saving archive content as file list: " + file.getAbsolutePath());
        try (ObjectOutputStream outputStream = new ObjectOutputStream(new BufferedOutputStream(new GzipCompressorOutputStream(new FileOutputStream(file))))) {
            outputStream.writeObject(filesystemItems.size());
            for (FilesystemItem item : filesystemItems) {
            for (BorgFilesystemItem item : filesystemItems) {
                outputStream.writeObject(item);
            }
            outputStream.writeObject("EOF");
@@ -57,7 +57,7 @@
     * @param archive
     * @return
     */
    public FilesystemItem[] load(BorgRepoConfig repoConfig, Archive archive) {
    public BorgFilesystemItem[] load(BorgRepoConfig repoConfig, BorgArchive archive) {
        File file = getFile(repoConfig, archive);
        if (!file.exists()) {
            return null;
@@ -65,13 +65,13 @@
        return load(file);
    }
    public FilesystemItem[] load(File file) {
    public BorgFilesystemItem[] load(File file) {
        if (file.exists() == false) {
            log.error("File '" + file.getAbsolutePath() + "' doesn't exist. Can't get archive content files.");
            return null;
        }
        log.info("Loading archive content as file list from: " + file.getAbsolutePath());
        FilesystemItem[] list = null;
        BorgFilesystemItem[] list = null;
        try {
            // Set last modified time of file:
            Files.setAttribute(file.toPath(), "lastModifiedTime", FileTime.fromMillis(System.currentTimeMillis()));
@@ -85,11 +85,11 @@
                return null;
            }
            int size = (Integer) obj;
            list = new FilesystemItem[size];
            list = new BorgFilesystemItem[size];
            for (int i = 0; i < size; i++) {
                obj = inputStream.readObject();
                if (obj instanceof FilesystemItem) {
                    list[i] = (FilesystemItem) obj;
                if (obj instanceof BorgFilesystemItem) {
                    list[i] = (BorgFilesystemItem) obj;
                } else {
                    log.error("Can't load archive content. FilesystemItem expected, but received: " + obj.getClass()
                            + " at position " + i + ".");
@@ -105,7 +105,7 @@
    /**
     * Deletes archive contents older than 7 days and deletes the oldest archive contents if the max cache size is
     * exceeded. The last modified time of a file is equals to the last usage by {@link #load(BorgRepoConfig, Archive)}.
     * exceeded. The last modified time of a file is equals to the last usage by {@link #load(BorgRepoConfig, BorgArchive)}.
     */
    public void cleanUp() {
        File[] files = cacheDir.listFiles();
@@ -180,7 +180,7 @@
        }
    }
    File getFile(BorgRepoConfig repoConfig, Archive archive) {
    File getFile(BorgRepoConfig repoConfig, BorgArchive archive) {
        return new File(cacheDir, ReplaceUtils.encodeFilename(CACHE_ARCHIVE_LISTS_BASENAME + archive.getTime()
                + "-" + repoConfig.getRepo() + "-" + archive.getArchive() + ".gz", true));
    }
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
@@ -4,6 +4,7 @@
import de.micromata.borgbutler.config.BorgRepoConfig;
import de.micromata.borgbutler.config.Configuration;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.data.Repository;
import de.micromata.borgbutler.json.borg.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.jcs.JCS;
@@ -22,8 +23,7 @@
    private static ButlerCache instance = new ButlerCache();
    private JCSCache jcsCache;
    private CacheAccess<String, RepoInfo> repoInfoCacheAccess;
    private CacheAccess<String, RepoList> repoListCacheAccess;
    private CacheAccess<String, Repository> repoCacheAccess;
    private ArchiveFilelistCache archiveFilelistCache;
    public static ButlerCache getInstance() {
@@ -31,55 +31,45 @@
    }
    public Repository getRepository(String idOrName) {
        RepoInfo repoInfo = getRepoInfo(idOrName);
        if (repoInfo == null) {
            return null;
        }
        return repoInfo.getRepository();
    }
    public RepoInfo getRepoInfo(String idOrName) {
        BorgRepoConfig repoConfig = ConfigurationHandler.getConfiguration().getRepoConfig(idOrName);
        RepoInfo repoInfo = repoInfoCacheAccess.get(repoConfig.getRepo());
        if (repoInfo == null ||repoInfo.getRepository() == null) {
            repoInfo = BorgCommands.info(repoConfig);
            repoInfoCacheAccess.put(repoConfig.getRepo(), repoInfo);
        Repository repository = repoCacheAccess.get(repoConfig.getRepo());
        if (repository == null ||repository.getLocation() == null) {
            repository = BorgCommands.info(repoConfig);
            repoCacheAccess.put(repoConfig.getRepo(), repository);
        }
        if (repoInfo == null) {
        if (repository == null) {
            log.warn("Repo with name '" + idOrName + "' not found.");
        }
        return repoInfo;
        return repository;
    }
    public List<Repository> getAllRepositories() {
        List<Repository> repositories = new ArrayList<>();
        for (BorgRepoConfig repoConfig : ConfigurationHandler.getConfiguration().getRepoConfigs()) {
            RepoInfo repoInfo = getRepoInfo(repoConfig.getName());
            if (repoInfo == null) {
            Repository repository = getRepository(repoConfig.getName());
            if (repository == null) {
                continue;
            }
            repositories.add(repoInfo.getRepository());
            repositories.add(repository);
        }
        return repositories;
    }
    public void clearAllCaches(){
        log.info("Clearing repositories cache (with list of archives)...");
        this.repoListCacheAccess.clear();
        clearRepoInfoCacheAccess();
        this.repoCacheAccess.clear();
        log.info("Clearing cache with file lists of archives...");
        this.archiveFilelistCache.removeAllCacheFiles();
    }
    public void clearRepoInfoCacheAccess() {
        log.info("Clearing repositories info cache...");
        this.repoInfoCacheAccess.clear();
    public void clearRepoCacheAccess() {
        log.info("Clearing repositories cache...");
        this.repoCacheAccess.clear();
    }
    public RepoList getRepoList(String idOrName) {
/*    public BorgRepoList getRepoList(String idOrName) {
        BorgRepoConfig repoConfig = ConfigurationHandler.getConfiguration().getRepoConfig(idOrName);
        //ArchiveInfo archiveInfo = BorgCommands.info(repoConfig, repoConfig.getRepo());
        RepoList repoList = repoListCacheAccess.get(repoConfig.getRepo());
        BorgRepoList repoList = repoListCacheAccess.get(repoConfig.getRepo());
        if (repoList == null) {
            repoList = BorgCommands.list(repoConfig);
            repoListCacheAccess.put(repoConfig.getRepo(), repoList);
@@ -88,18 +78,18 @@
            log.warn("Repo with name '" + idOrName + "' not found.");
        }
        return repoList;
    }
    }*/
    public FilesystemItem[] getArchiveContent(BorgRepoConfig repoConfig, Archive archive) {
    public BorgFilesystemItem[] getArchiveContent(BorgRepoConfig repoConfig, BorgArchive archive) {
        if (archive == null || StringUtils.isBlank(archive.getArchive())) {
            return null;
        }
        FilesystemItem[] items = archiveFilelistCache.load(repoConfig, archive);
        BorgFilesystemItem[] items = archiveFilelistCache.load(repoConfig, archive);
        if (items == null) {
            List<FilesystemItem> list = BorgCommands.listArchiveContent(repoConfig, archive);
            List<BorgFilesystemItem> list = BorgCommands.listArchiveContent(repoConfig, archive);
            if (CollectionUtils.isNotEmpty(list)) {
                archiveFilelistCache.save(repoConfig, archive, list);
                items = list.toArray(new FilesystemItem[0]);
                items = list.toArray(new BorgFilesystemItem[0]);
            }
        }
        if (items == null) {
@@ -108,7 +98,7 @@
        return items;
    }
    public FilesystemItem[] getArchiveContent(File file) {
    public BorgFilesystemItem[] getArchiveContent(File file) {
        return archiveFilelistCache.load(file);
    }
@@ -123,8 +113,7 @@
    private ButlerCache() {
        Configuration configuration = ConfigurationHandler.getConfiguration();
        this.jcsCache = JCSCache.getInstance();
        this.repoInfoCacheAccess = jcsCache.getJCSCache("repoInfo");
        this.repoListCacheAccess = jcsCache.getJCSCache("repoList");
        this.repoCacheAccess = jcsCache.getJCSCache("repositories");
        this.archiveFilelistCache = new ArchiveFilelistCache(getCacheDir(), configuration.getCacheArchiveContentMaxDiscSizeMB());
    }
}
borgbutler-core/src/main/java/de/micromata/borgbutler/data/Repository.java
New file
@@ -0,0 +1,50 @@
package de.micromata.borgbutler.data;
import de.micromata.borgbutler.config.BorgRepoConfig;
import de.micromata.borgbutler.json.borg.BorgCache;
import de.micromata.borgbutler.json.borg.BorgEncryption;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
 * Part of Borg json objects to refer objects to repositories.
 */
public class Repository implements Serializable {
    private static final long serialVersionUID = 1278802519434516280L;
    /**
     * A name describing this config. Only used for displaying purposes. This is automatically set with the name
     * of the repository configuration.
     *
     * @see BorgRepoConfig#getName()
     */
    @Getter
    @Setter
    String name;
    @Getter
    @Setter
    private String id;
    /**
     * UTC date.
     */
    @Getter
    @Setter
    private String lastModified;
    @Getter
    @Setter
    private String location;
    @Getter
    @Setter
    private String securityDir;
    @Getter
    @Setter
    private BorgCache cache;
    @Getter
    @Setter
    private BorgEncryption encryption;
    public Repository() {
    }
}
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgArchive.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/Archive.java
@@ -10,7 +10,7 @@
/**
 * This object is given by <tt>borg list repo</tt>.
 */
public class Archive implements Serializable {
public class BorgArchive implements Serializable {
    private static final long serialVersionUID = -7872260170265536732L;
    @Getter
    private String archive;
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgArchive2.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/Archive2.java
@@ -9,7 +9,7 @@
/**
 * This object is given by <tt>borg list archive</tt>.
 */
public class Archive2 implements Serializable {
public class BorgArchive2 implements Serializable {
    private static final long serialVersionUID = 4734056884088174992L;
    @Getter
    @JsonProperty("chunker_params")
@@ -25,7 +25,7 @@
    @Getter
    private String start;
    @Getter
    private ArchiveStats stats;
    private BorgArchiveStats stats;
    @Getter
    private String username;
    public String toString() {
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgArchiveInfo.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/ArchiveInfo.java
@@ -10,16 +10,16 @@
/**
 * Result of <tt>borg info repo::archive</tt>.
 */
public class ArchiveInfo implements Serializable {
public class BorgArchiveInfo implements Serializable {
    private static final long serialVersionUID = -4200553322856662346L;
    @Getter
    private List<Archive2> archives;
    private List<BorgArchive2> archives;
    @Getter
    private Cache cache;
    private BorgCache cache;
    @Getter
    private Encryption encryption;
    private BorgEncryption encryption;
    @Getter
    private Repository repository;
    private BorgRepository repository;
    @Getter
    @Setter
    @JsonIgnore
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgArchiveStats.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/ArchiveStats.java
@@ -5,7 +5,7 @@
import java.io.Serializable;
public class ArchiveStats implements Serializable {
public class BorgArchiveStats implements Serializable {
    private static final long serialVersionUID = -7603297185652222010L;
    @Getter
    @JsonProperty("compressed_size")
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgCache.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/Cache.java
@@ -4,10 +4,10 @@
import java.io.Serializable;
public class Cache implements Serializable {
public class BorgCache implements Serializable {
    private static final long serialVersionUID = -1728825838475013561L;
    @Getter
    private String path;
    @Getter
    private Stats stats;
    private BorgStats stats;
}
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgEncryption.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/Encryption.java
@@ -4,7 +4,7 @@
import java.io.Serializable;
public class Encryption implements Serializable {
public class BorgEncryption implements Serializable {
    private static final long serialVersionUID = -4867140003118289187L;
    @Getter
    private String mode;
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgFilesystemItem.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/FilesystemItem.java
@@ -4,7 +4,7 @@
import java.io.Serializable;
public class FilesystemItem implements Serializable {
public class BorgFilesystemItem implements Serializable {
    private static final long serialVersionUID = -5545350851640655468L;
    /**
     * d (directory), - (file)
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgRepoInfo.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/RepoInfo.java
@@ -10,17 +10,17 @@
/**
 * Result of borg info repo
 */
public class RepoInfo implements Serializable {
public class BorgRepoInfo implements Serializable {
    private static final long serialVersionUID = -1588038325129799400L;
    @Getter
    @JsonProperty("security_dir")
    private String securityDir;
    @Getter
    private Cache cache;
    private BorgCache cache;
    @Getter
    private Encryption encryption;
    private BorgEncryption encryption;
    @Getter
    private Repository repository;
    private BorgRepository repository;
    @Getter
    @Setter
    @JsonIgnore
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgRepoList.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/RepoList.java
@@ -10,14 +10,14 @@
/**
 * Result of borg list repo
 */
public class RepoList implements Serializable {
public class BorgRepoList implements Serializable {
    private static final long serialVersionUID = 1006757749929526034L;
    @Getter
    private List<Archive> archives;
    private List<BorgArchive> archives;
    @Getter
    private Encryption encryption;
    private BorgEncryption encryption;
    @Getter
    private Repository repository;
    private BorgRepository repository;
    @Getter
    @Setter
    @JsonIgnore
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgRepository.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/Repository.java
@@ -6,13 +6,10 @@
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
 * Part of Borg json objects to refer objects to repositories.
 */
public class Repository implements Serializable {
    private static final long serialVersionUID = 1278802519434516280L;
public class BorgRepository {
    /**
     * A name describing this config. Only used for displaying purposes. This is automatically set with the name
     * of the repository configuration.
@@ -36,7 +33,7 @@
     * @param location
     * @return
     */
    public Repository setLocation(String location) {
    public BorgRepository setLocation(String location) {
        this.location = location;
        // It's ugly but efficiently ;-)
        BorgRepoConfig repoConfig = ConfigurationHandler.getConfiguration().getRepoConfig(location);
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgStats.java
File was renamed from borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/Stats.java
@@ -5,7 +5,7 @@
import java.io.Serializable;
public class Stats implements Serializable {
public class BorgStats implements Serializable {
    private static final long serialVersionUID = 9141985857856734073L;
    @Getter
    @JsonProperty("total_chunks")
borgbutler-core/src/main/java/de/micromata/borgbutler/utils/DateUtils.java
New file
@@ -0,0 +1,15 @@
package de.micromata.borgbutler.utils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateUtils {
    /**
     * @param borgDateTime
     * @return
     */
    public static String get(String borgDateTime) {
        LocalDateTime dateTime = LocalDateTime.parse(borgDateTime);
        return dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }
}
borgbutler-core/src/test/java/de/micromata/borgbutler/cache/ArchiveFilelistCacheTest.java
@@ -1,8 +1,8 @@
package de.micromata.borgbutler.cache;
import de.micromata.borgbutler.config.BorgRepoConfig;
import de.micromata.borgbutler.json.borg.Archive;
import de.micromata.borgbutler.json.borg.FilesystemItem;
import de.micromata.borgbutler.json.borg.BorgArchive;
import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,17 +24,17 @@
    @Test
    void readWriteTest() throws Exception {
        List<FilesystemItem> list = createList(1000000);
        List<BorgFilesystemItem> list = createList(1000000);
        ArchiveFilelistCache cache = new ArchiveFilelistCache(new File("out"), 100);
        cache.removeAllCacheFiles();
        BorgRepoConfig repoConfig = new BorgRepoConfig();
        repoConfig.setRepo("repo");
        Archive archive = createArchive("2018-12-10");
        BorgArchive archive = createArchive("2018-12-10");
        log.info("Saving " + list.size() + " items to out dir.");
        cache.save(repoConfig, archive, list);
        log.info("Saving done.");
        log.info("Loading items from out dir.");
        FilesystemItem[] filesystemItems = cache.load(repoConfig, archive);
        BorgFilesystemItem[] filesystemItems = cache.load(repoConfig, archive);
        log.info("Loading " + filesystemItems.length + " items done.");
        assertEquals(list.size(), filesystemItems.length);
        for (int i = 0; i < filesystemItems.length; i++) {
@@ -45,22 +45,22 @@
    @Test
    void readWriteEmptyTest() throws Exception {
        List<FilesystemItem> list = new ArrayList<>();
        List<BorgFilesystemItem> list = new ArrayList<>();
        ArchiveFilelistCache cache = new ArchiveFilelistCache(new File("out"), 100);
        cache.removeAllCacheFiles();
        BorgRepoConfig repoConfig = new BorgRepoConfig();
        repoConfig.setRepo("repo");
        Archive archive = createArchive("2018-12-09");
        BorgArchive archive = createArchive("2018-12-09");
        assertNull(cache.load(repoConfig, archive));
        cache.save(repoConfig, archive, list);
        FilesystemItem[] filesystemItems = cache.load(repoConfig, archive);
        BorgFilesystemItem[] filesystemItems = cache.load(repoConfig, archive);
        assertNull(cache.load(repoConfig, archive));
        cache.removeAllCacheFiles();
    }
    @Test
    void cleanUpMaximumSizeTest() throws Exception {
        List<FilesystemItem> list = createList(1000000);
        List<BorgFilesystemItem> list = createList(1000000);
        ArchiveFilelistCache cache = new ArchiveFilelistCache(new File("out"), 3);
        cache.removeAllCacheFiles();
        BorgRepoConfig repoConfig = new BorgRepoConfig();
@@ -68,7 +68,7 @@
        long millis = System.currentTimeMillis();
        Archive archive = createArchive("2018-11-20");
        BorgArchive archive = createArchive("2018-11-20");
        cache.save(repoConfig, archive, list);
        File oldestFile = cache.getFile(repoConfig, archive);
        setLastModificationTime(oldestFile, millis - 10 * 3600000); // Fake lastModifiedTime - 10 h
@@ -96,7 +96,7 @@
    @Test
    void cleanUpExpiredTest() throws Exception {
        List<FilesystemItem> list = createList(1000);
        List<BorgFilesystemItem> list = createList(1000);
        ArchiveFilelistCache cache = new ArchiveFilelistCache(new File("out"), 3);
        cache.removeAllCacheFiles();
        BorgRepoConfig repoConfig = new BorgRepoConfig();
@@ -104,7 +104,7 @@
        long millis = System.currentTimeMillis();
        Archive archive = createArchive("2018-10-20");
        BorgArchive archive = createArchive("2018-10-20");
        cache.save(repoConfig, archive, list);
        File notExpiredFile = cache.getFile(repoConfig, archive);
        setLastModificationTime(notExpiredFile, millis - 6 * 24 * 3600000); // Fake lastModifiedTime - 10 h
@@ -123,16 +123,16 @@
        cache.removeAllCacheFiles();
    }
    private List<FilesystemItem> createList(int number) throws Exception {
        List<FilesystemItem> list = new ArrayList<>();
    private List<BorgFilesystemItem> createList(int number) throws Exception {
        List<BorgFilesystemItem> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            list.add(create(i));
        }
        return list;
    }
    private FilesystemItem create(int i) throws Exception {
        FilesystemItem item = new FilesystemItem();
    private BorgFilesystemItem create(int i) throws Exception {
        BorgFilesystemItem item = new BorgFilesystemItem();
        set(item, "type", "-").set(item, "mode", "drwxr-xr-x")
                .set(item, "user", "kai").set(item, "group", "user")
                .set(item, "path", "/Users/kai/Test" + i + ".java").set(item, "size", 1000);
@@ -146,8 +146,8 @@
        return this;
    }
    private Archive createArchive(String time) throws Exception {
        Archive archive = new Archive();
    private BorgArchive createArchive(String time) throws Exception {
        BorgArchive archive = new BorgArchive();
        set(archive, "archive", "archive-" + time);
        set(archive, "time", time);
        return archive;
borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java
@@ -3,11 +3,9 @@
import de.micromata.borgbutler.config.BorgRepoConfig;
import de.micromata.borgbutler.config.Configuration;
import de.micromata.borgbutler.config.ConfigurationHandler;
import de.micromata.borgbutler.json.borg.Archive;
import de.micromata.borgbutler.json.borg.FilesystemItem;
import de.micromata.borgbutler.json.borg.RepoInfo;
import de.micromata.borgbutler.json.borg.RepoList;
import org.apache.commons.collections4.CollectionUtils;
import de.micromata.borgbutler.data.Repository;
import de.micromata.borgbutler.json.borg.BorgArchive;
import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,29 +29,29 @@
        ButlerCache butlerCache = ButlerCache.getInstance();
        {
            for (BorgRepoConfig repoConfig : ConfigurationHandler.getConfiguration().getRepoConfigs()) {
                RepoInfo repoInfo = ButlerCache.getInstance().getRepoInfo(repoConfig.getRepo());
                Repository repository = ButlerCache.getInstance().getRepository(repoConfig.getRepo());
            }
            assertEquals(config.getRepoConfigs().size(), ButlerCache.getInstance().getAllRepositories().size());
        }
        {
/*        {
            for (BorgRepoConfig repoConfig : ConfigurationHandler.getConfiguration().getRepoConfigs()) {
                RepoList repoList = ButlerCache.getInstance().getRepoList(repoConfig.getRepo());
                BorgRepoList repoList = ButlerCache.getInstance().getRepoList(repoConfig.getRepo());
            }
            assertEquals(config.getRepoConfigs().size(), ButlerCache.getInstance().getAllRepositories().size());
        }
        }*/
        List<BorgRepoConfig> repoConfigs = ConfigurationHandler.getConfiguration().getRepoConfigs();
        Archive archive = null;
        BorgArchive archive = null;
        BorgRepoConfig repoConfig = null;
        if (CollectionUtils.isNotEmpty(repoConfigs)) {
/*        if (CollectionUtils.isNotEmpty(repoConfigs)) {
            repoConfig = repoConfigs.get(0);
            RepoList repoList = ButlerCache.getInstance().getRepoList(repoConfig.getRepo());
            BorgRepoList repoList = ButlerCache.getInstance().getRepoList(repoConfig.getRepo());
            if (repoList != null && CollectionUtils.isNotEmpty(repoList.getArchives())) {
                archive = repoList.getArchives().get(0);
            }
        }
        }*/
        {
            if (archive != null) {
                FilesystemItem[] content = ButlerCache.getInstance().getArchiveContent(repoConfig, archive);
                BorgFilesystemItem[] content = ButlerCache.getInstance().getArchiveContent(repoConfig, archive);
                log.info("Number of items (content) of archive: " + content.length);
                content = ButlerCache.getInstance().getArchiveContent(repoConfig, archive);
                log.info("Number of items (content) of archive: " + content.length);
borgbutler-core/src/test/java/de/micromata/borgbutler/utils/DateUtilsTest.java
New file
@@ -0,0 +1,25 @@
package de.micromata.borgbutler.utils;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDateTime;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class DateUtilsTest {
    private static Logger log = LoggerFactory.getLogger(DateUtilsTest.class);
    @Test
    void parseTest() {
        LocalDateTime dateTime = DateUtils.get("2018-11-21T22:31:51.000000");
        assertEquals(2018, dateTime.getYear());
        assertEquals(11, dateTime.getMonthValue());
        assertEquals(21, dateTime.getDayOfMonth());
        assertEquals(22, dateTime.getHour());
        assertEquals(31, dateTime.getMinute());
        assertEquals(51, dateTime.getSecond());
    }
}
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 de.micromata.borgbutler.json.borg.FilesystemItem;
import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
import de.micromata.borgbutler.server.jetty.JettyServer;
import de.micromata.borgbutler.server.user.SingleUserManager;
import de.micromata.borgbutler.server.user.UserManager;
@@ -132,7 +132,7 @@
    private static void printArchiveContent(String fileName) {
        File file = new File(fileName);
        FilesystemItem[] fileList = ButlerCache.getInstance().getArchiveContent(file);
        BorgFilesystemItem[] fileList = ButlerCache.getInstance().getArchiveContent(file);
        boolean parseFormatExceptionPrinted = false;
        if (fileList != null && fileList.length > 0) {
            TimeZone tz = TimeZone.getTimeZone("UTC");
@@ -142,7 +142,7 @@
            File out = new File(FilenameUtils.getBaseName(fileName) + ".txt.gz");
            log.info("Writing file list to: " + out.getAbsolutePath());
            try (PrintWriter writer = new PrintWriter(new BufferedOutputStream(new GzipCompressorOutputStream(new FileOutputStream(out))))) {
                for (FilesystemItem item : fileList) {
                for (BorgFilesystemItem item : fileList) {
                    String time = item.getMtime();
                    try {
                        Date date = df.parse(item.getMtime());
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ReposRest.java
@@ -1,8 +1,9 @@
package de.micromata.borgbutler.server.rest;
import de.micromata.borgbutler.cache.ButlerCache;
import de.micromata.borgbutler.data.Repository;
import de.micromata.borgbutler.json.JsonUtils;
import de.micromata.borgbutler.json.borg.Repository;
import de.micromata.borgbutler.json.borg.BorgRepository;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,12 +40,12 @@
     *
     * @param force If true, a reload of all repositories is forced.
     * @param prettyPrinter If true then the json output will be in pretty format.
     * @return A list of repositories of type {@link Repository}.
     * @return A list of repositories of type {@link BorgRepository}.
     * @see JsonUtils#toJson(Object, boolean)
     */
    public String getList(@QueryParam("force") boolean force, @QueryParam("prettyPrinter") boolean prettyPrinter) {
        if (force) {
            ButlerCache.getInstance().clearRepoInfoCacheAccess();
            ButlerCache.getInstance().clearRepoCacheAccess();
        }
        List<Repository> repositories = ButlerCache.getInstance().getAllRepositories();
        if (CollectionUtils.isEmpty(repositories)) {
borgbutler-webapp/src/components/views/repos/RepoArchiveList.jsx
New file
@@ -0,0 +1,104 @@
import React from 'react'
import {PageHeader} from '../../general/BootstrapComponents';
import {getRestServiceUrl} from '../../../utilities/global';
import ErrorAlert from '../../general/ErrorAlert';
import {IconRefresh} from "../../general/IconComponents";
class RepoListView extends React.Component {
    path = getRestServiceUrl('repos');
    state = {
        isFetching: false
    };
    componentDidMount = () => {
        this.fetchRepos();
    };
    fetchRepos = (force) => {
        this.setState({
            isFetching: true,
            failed: false,
            repos: undefined
        });
        fetch(`${this.path}/list?force=${force}`, {
            method: 'GET',
            headers: {
                'Accept': 'application/json'
            }
        })
            .then(response => response.json())
            .then(json => {
                const repos = json.map(repo => {
                    return {
                        id: repo.id,
                        name: repo.name,
                        location: repo.location,
                        lastModified: repo.last_modified
                    };
                });
                this.setState({
                    isFetching: false,
                    repos
                })
            })
            .catch(() => this.setState({isFetching: false, failed: true}));
    };
    render = () => {
        let content = undefined;
        if (this.state.isFetching) {
            content = <i>Loading...</i>;
        } else if (this.state.failed) {
            content = <ErrorAlert
                title={'Cannot load Repositories'}
                description={'Something went wrong during contacting the rest api.'}
                action={{
                    handleClick: this.fetchRepos,
                    title: 'Try again'
                }}
            />;
        } else if (this.state.repos) {
            content = <React.Fragment>
                <div
                    className={'btn btn-outline-primary refresh-button-right'}
                    onClick={this.fetchRepos.bind(this, true)}
                >
                    <IconRefresh/>
                </div>
                <CardDeck>
                {this.state.repos.map(repo => {
                    return <RepoCard
                        key={repo.id}
                        repo={repo}
                    />;
                })}
                </CardDeck>
            </React.Fragment>;
        }
        return <React.Fragment>
            <PageHeader>
                Repositories
            </PageHeader>
            {content}
        </React.Fragment>;
    };
    constructor(props) {
        super(props);
        this.fetchRepos = this.fetchRepos.bind(this);
    }
}
export default RepoListView;
borgbutler-webapp/src/components/views/repos/RepoCard.jsx
@@ -27,7 +27,7 @@
                        {repoText}
                    </ul>
                </CardBody>
                <CardFooter><span className={'lastModified'}>Last modified: {formatDateTime(repo.lastModified)}</span></CardFooter>
                <CardFooter><span className={'lastModified'}>Last modified: {repo.lastModified}</span></CardFooter>
            </Card>
        </React.Fragment>
    };
borgbutler-webapp/src/components/views/repos/RepoListView.jsx
@@ -38,7 +38,7 @@
                        id: repo.id,
                        name: repo.name,
                        location: repo.location,
                        lastModified: repo.last_modified
                        lastModified: repo.lastModified
                    };
                });