All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
de.carne.boot.prefs.FilePreferencesStore Maven / Gradle / Ivy
/*
* Copyright (c) 2018-2019 Holger de Carne and contributors, All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package de.carne.boot.prefs;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import de.carne.boot.ShutdownHooks;
import de.carne.boot.logging.Log;
/**
* This class provides the actual store access for all {@linkplain FilePreferences} objects.
*/
abstract class FilePreferencesStore {
private static final Log LOG = new Log();
private static final Set FILE_STORES = new HashSet<>();
private @Nullable Properties cachedData = null;
private List> changeLog = new LinkedList<>();
public static FilePreferencesStore fromFile(Path file) {
FileStore fileStore = new FileStore(file);
synchronized (FILE_STORES) {
if (FILE_STORES.isEmpty()) {
ShutdownHooks.add(FilePreferencesStore::flushFileStores);
}
FILE_STORES.add(fileStore);
}
return fileStore;
}
public static void flushFileStores() {
LOG.info("Flushing all opended file stores...");
synchronized (FILE_STORES) {
for (FileStore fileStore : FILE_STORES) {
try {
fileStore.flush();
} catch (BackingStoreException e) {
LOG.error(e, "Failed to store configuration file: ''{0}''", fileStore);
}
}
}
}
public static FilePreferencesStore fromData(Properties data) {
return new DataStore(data);
}
public FilePreferences root() {
return new FilePreferences(this);
}
public synchronized void put(FilePreferences preferences, String key, String value) {
String preferencesKey = getPreferencesKey(preferences, key);
recordChange(data -> data.put(preferencesKey, value));
}
@Nullable
public synchronized String get(FilePreferences preferences, String key) {
String preferencesKey = getPreferencesKey(preferences, key);
return getCachedData().getProperty(preferencesKey);
}
public synchronized void remove(FilePreferences preferences, String key) {
String preferencesKey = getPreferencesKey(preferences, key);
recordChange(data -> data.remove(preferencesKey));
}
public synchronized void removeNode(FilePreferences preferences) {
String preferencesKeyPrefix = getPreferencesKey(preferences, "");
recordChange(data -> {
@SuppressWarnings("null") Iterator> entryIterator = data.entrySet().iterator();
while (entryIterator.hasNext()) {
if (entryIterator.next().getKey().toString().startsWith(preferencesKeyPrefix)) {
entryIterator.remove();
}
}
});
}
@SuppressWarnings("null")
public synchronized String[] keys(FilePreferences preferences) {
String preferencesKeyPrefix = getPreferencesKey(preferences, "");
Set<@NonNull String> keys = getCachedData().keySet().stream().map(Object::toString)
.map(key -> extractKey(preferencesKeyPrefix, key)).filter(s -> !s.isEmpty())
.collect(Collectors.toSet());
return keys.toArray(new String[keys.size()]);
}
@SuppressWarnings("null")
public synchronized String[] childrenNames(FilePreferences preferences) {
String preferencesKeyPrefix = getPreferencesKey(preferences, "");
Set childrenNames = getCachedData().keySet().stream().map(Object::toString)
.map(key -> extractChildrenName(preferencesKeyPrefix, key)).filter(s -> !s.isEmpty())
.collect(Collectors.toSet());
return childrenNames.toArray(new String[childrenNames.size()]);
}
public synchronized FilePreferences child(FilePreferences preferences, String name) {
return new FilePreferences(preferences, name);
}
public synchronized void sync() throws BackingStoreException {
LOG.info("Syncing preferences store ''{0}''...", this);
try {
this.cachedData = syncData(this.changeLog);
} catch (IOException e) {
throw new BackingStoreException(e);
}
this.changeLog.clear();
}
public synchronized void flush() throws BackingStoreException {
if (!this.changeLog.isEmpty()) {
LOG.info("FLushing preferences store ''{0}''...", this);
try {
syncData(this.changeLog);
} catch (IOException e) {
throw new BackingStoreException(e);
}
this.changeLog.clear();
}
}
private void recordChange(Consumer change) {
Properties data = getCachedData();
change.accept(data);
this.changeLog.add(change);
}
private String getPreferencesKey(Preferences preferences, String key) {
StringBuilder keyBuilder = new StringBuilder();
keyBuilder.append(preferences.absolutePath());
if (preferences.parent() != null) {
keyBuilder.append('/');
}
keyBuilder.append(key);
return keyBuilder.toString();
}
private String extractKey(String prefix, String preferencesKey) {
String key = "";
if (preferencesKey.startsWith(prefix)) {
int keyIndex = prefix.length();
if (preferencesKey.indexOf('/', keyIndex) < 0) {
key = preferencesKey.substring(keyIndex);
}
}
return key;
}
private String extractChildrenName(String prefix, String preferencesKey) {
String childrenName = "";
if (preferencesKey.startsWith(prefix)) {
int childrenNameIndex = prefix.length();
int nextIndex = preferencesKey.indexOf('/', childrenNameIndex);
if (nextIndex > childrenNameIndex) {
childrenName = preferencesKey.substring(childrenNameIndex, nextIndex);
}
}
return childrenName;
}
private Properties getCachedData() {
Properties data = this.cachedData;
if (data == null) {
try {
data = loadData();
} catch (IOException e) {
LOG.error(e, "Failed to load configuration file: ''{0}''", this);
data = new Properties();
}
this.cachedData = data;
}
return data;
}
protected abstract Properties loadData() throws IOException;
protected abstract Properties syncData(List> changes) throws IOException;
}