borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractCache.java
@@ -1,7 +1,6 @@ package de.micromata.borgbutler.cache; import com.fasterxml.jackson.annotation.JsonIgnore; import de.micromata.borgbutler.config.BorgRepoConfig; import de.micromata.borgbutler.config.Definitions; import de.micromata.borgbutler.json.JsonUtils; import lombok.Getter; @@ -15,73 +14,40 @@ import java.io.*; import java.util.Date; import java.util.HashMap; import java.util.Map; public abstract class AbstractCache<T> { public abstract class AbstractCache { private static Logger log = LoggerFactory.getLogger(AbstractCache.class); private static final String CACHE_FILE_PREFIX = "cache-"; private static final String CACHE_FILE_EXTENSION = "json"; private static final String CACHE_FILE_ZIP_EXTENSION = ".zip"; private static final String CACHE_FILE_ZIP_EXTENSION = ".gz"; /** * INITIAL - on startup (not yet read), DIRTY - modifications not written, SAVED - content written to file. */ private enum STATE {INITIAL, DIRTY, SAVED} protected enum STATE {INITIAL, DIRTY, SAVED} @JsonIgnore protected File cacheFile; @JsonIgnore @Getter private boolean zip; private boolean compress; @Getter @JsonIgnore private STATE state = STATE.INITIAL; @Getter private Date lastModified; @Getter private Date created; @Getter protected Map<String, T> elements = new HashMap<>(); public T get(BorgRepoConfig repoConfig, String identifier) { if (identifier == null) { return null; } if (this.state == STATE.INITIAL) { read(); } for (T element : elements.values()) { if (matches(element, identifier)) { return element; } } return load(repoConfig, identifier); } protected abstract T load(BorgRepoConfig repoConfig, String identifier); public abstract boolean matches(T element, String identifier); public abstract String getIdentifier(T element); public abstract void updateFrom(T dest, T source); /** * Removes all entries (doesn't effect the cache files!). */ public void clear() { elements.clear(); state = STATE.DIRTY; } public void upsert(BorgRepoConfig repoConfig, T element) { T existingElement = get(repoConfig, getIdentifier(element)); if (existingElement == null) { elements.put(getIdentifier(element), element); } else { updateFrom(existingElement, element); } state = STATE.DIRTY; // Needed to save cache to file. protected void setDirty() { state = STATE.DIRTY; } public void read() { @@ -93,7 +59,7 @@ } log.info("Parsing cache file '" + cacheFile.getAbsolutePath() + "'."); String json; if (zip) { if (compress) { try (GzipCompressorInputStream in = new GzipCompressorInputStream(new FileInputStream(cacheFile))) { StringWriter writer = new StringWriter(); IOUtils.copy(in, writer, Definitions.STD_CHARSET); @@ -104,10 +70,10 @@ } AbstractCache readCache = JsonUtils.fromJson(this.getClass(), json); if (readCache != null) { this.elements = readCache.elements; this.lastModified = readCache.lastModified; this.created = readCache.created; this.state = STATE.SAVED; // State of cache is updated from cache file. update(readCache); } else { log.error("Error while parsing cache: " + cacheFile.getAbsolutePath()); this.state = STATE.DIRTY; // Needed to save cache to file. @@ -119,6 +85,8 @@ } } protected abstract void update(AbstractCache readCache); public void save() { if (this.state == STATE.SAVED || this.state == STATE.INITIAL) { log.info("Cache file is up to date (nothing to save): " + cacheFile); @@ -132,7 +100,7 @@ } String json = JsonUtils.toJson(this); try { if (this.zip) { if (this.compress) { try (GzipCompressorOutputStream out = new GzipCompressorOutputStream(new FileOutputStream(cacheFile))) { IOUtils.copy(new StringReader(json), out, Definitions.STD_CHARSET); } @@ -158,9 +126,9 @@ } AbstractCache(File cacheDir, String cacheFilename, boolean zip) { this.zip = zip; this.compress = zip; String filename = CACHE_FILE_PREFIX + cacheFilename + "." + CACHE_FILE_EXTENSION; if (this.zip) if (this.compress) filename = filename + CACHE_FILE_EXTENSION; cacheFile = new File(cacheDir, CACHE_FILE_PREFIX + cacheFilename + "." + CACHE_FILE_EXTENSION); this.state = STATE.INITIAL; borgbutler-core/src/main/java/de/micromata/borgbutler/cache/AbstractElementsCache.java
New file @@ -0,0 +1,76 @@ package de.micromata.borgbutler.cache; import de.micromata.borgbutler.config.BorgRepoConfig; import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.util.HashMap; import java.util.Map; public abstract class AbstractElementsCache<T> extends AbstractCache { private static Logger log = LoggerFactory.getLogger(AbstractElementsCache.class); @Getter protected Map<String, T> elements = new HashMap<>(); public T get(BorgRepoConfig repoConfig, String identifier) { if (identifier == null) { return null; } if (getState() == STATE.INITIAL) { read(); } for (T element : elements.values()) { if (matches(element, identifier)) { return element; } } return load(repoConfig, identifier); } protected abstract T load(BorgRepoConfig repoConfig, String identifier); public abstract boolean matches(T element, String identifier); public abstract String getIdentifier(T element); public abstract void updateFrom(T dest, T source); /** * Removes all entries (doesn't effect the cache files!). */ public void clear() { elements.clear(); super.clear(); } public void upsert(BorgRepoConfig repoConfig, T element) { T existingElement = get(repoConfig, getIdentifier(element)); if (existingElement == null) { elements.put(getIdentifier(element), element); } else { updateFrom(existingElement, element); } setDirty(); } protected void update(AbstractCache readCache) { this.elements = ((AbstractElementsCache)readCache).elements; } /** * Needed by jackson for deserialization. */ AbstractElementsCache() { } AbstractElementsCache(File cacheDir, String cacheFilename) { super(cacheDir, cacheFilename); } AbstractElementsCache(File cacheDir, String cacheFilename, boolean compress) { super(cacheDir, cacheFilename, compress); } } borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ArchiveListCache.java
@@ -8,7 +8,7 @@ import java.io.File; public class ArchiveListCache extends AbstractCache<ArchiveInfo> { public class ArchiveListCache extends AbstractElementsCache<ArchiveInfo> { private static Logger log = LoggerFactory.getLogger(ArchiveListCache.class); public static final String CACHE_ARCHIVE_LISTS_BASENAME = "archive-lists"; borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
@@ -1,6 +1,7 @@ package de.micromata.borgbutler.cache; import com.fasterxml.jackson.annotation.JsonIgnore; import de.micromata.borgbutler.config.ConfigurationHandler; import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,7 +12,7 @@ public class ButlerCache { private static Logger log = LoggerFactory.getLogger(ButlerCache.class); public static final String CACHE_DIR_NAME = ".borgbutler"; public static final String CACHE_DIR_NAME = "caches"; private static ButlerCache instance = new ButlerCache(); @Getter @@ -19,7 +20,7 @@ @Getter private RepoListCache repoListCache; private ArchiveListCache archiveListCache; private List<AbstractCache> caches; private List<AbstractElementsCache> caches; @JsonIgnore private File cacheDir; @@ -29,13 +30,13 @@ } public void read() { for (AbstractCache cache : caches) { for (AbstractElementsCache cache : caches) { cache.read(); } } public void save() { for (AbstractCache cache : caches) { for (AbstractElementsCache cache : caches) { cache.save(); } } @@ -46,19 +47,18 @@ public void removeAllCacheFiles() { File[] files = cacheDir.listFiles(); for (File file : files) { if (AbstractCache.isCacheFile(file)) { if (AbstractElementsCache.isCacheFile(file)) { log.info("Deleting cache file: " + file.getAbsolutePath()); file.delete(); } } for (AbstractCache cache : caches) { for (AbstractElementsCache cache : caches) { cache.clear(); } } private ButlerCache() { String homeDir = System.getProperty("user.home"); cacheDir = new File(homeDir, CACHE_DIR_NAME); cacheDir = new File(ConfigurationHandler.getInstance().getWorkingDir(), CACHE_DIR_NAME); if (!cacheDir.exists()) { log.info("Creating cache dir: " + cacheDir.getAbsolutePath()); cacheDir.mkdir(); borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoInfoCache.java
@@ -8,7 +8,7 @@ import java.io.File; public class RepoInfoCache extends AbstractCache<RepoInfo> { public class RepoInfoCache extends AbstractElementsCache<RepoInfo> { private static Logger log = LoggerFactory.getLogger(RepoInfoCache.class); public static final String CACHE_REPOS_BASENAME = "repo-infos"; borgbutler-core/src/main/java/de/micromata/borgbutler/cache/RepoListCache.java
@@ -8,7 +8,7 @@ import java.io.File; public class RepoListCache extends AbstractCache<RepoList> { public class RepoListCache extends AbstractElementsCache<RepoList> { private static Logger log = LoggerFactory.getLogger(RepoListCache.class); public static final String CACHE_REPO_LISTS_BASENAME = "repo-lists"; borgbutler-core/src/main/java/de/micromata/borgbutler/config/ConfigurationHandler.java
@@ -12,11 +12,13 @@ public class ConfigurationHandler { private static Logger log = LoggerFactory.getLogger(ConfigurationHandler.class); private static ConfigurationHandler instance = new ConfigurationHandler(); private static final String APP_WORKING_DIR = ".borgbutler"; private static final String CONFIG_FILENAME = ".borgbutler.json"; private static final String CONFIG_BACKUP_FILENAME = ".borgbutler-bak.json"; @Getter private File configFile; private File backupConfigFile; private File workingDir; private Configuration configuration = new Configuration(); public static ConfigurationHandler getInstance() { @@ -53,8 +55,18 @@ } } public File getWorkingDir() { if (!workingDir.exists()) { log.info("Creating borg-butlers working directory: " + workingDir.getAbsolutePath()); workingDir.mkdirs(); } return workingDir; } private ConfigurationHandler() { configFile = new File(System.getProperty("user.home"), CONFIG_FILENAME); backupConfigFile = new File(System.getProperty("user.home"), CONFIG_BACKUP_FILENAME); File userHome = new File(System.getProperty("user.home")); configFile = new File(userHome, CONFIG_FILENAME); backupConfigFile = new File(userHome, CONFIG_BACKUP_FILENAME); workingDir = new File(userHome, APP_WORKING_DIR); } }