mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 15:10:23 +00:00
Rework storage error handling
This commit is contained in:
parent
8ac507a3b5
commit
2fb2ce35a0
6 changed files with 45 additions and 102 deletions
|
@ -64,17 +64,11 @@ public class DataStoreProviders {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends DataStoreProvider> T byStore(DataStore store) {
|
||||
return (T) byStoreClass(store.getClass()).orElseThrow();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends DataStoreProvider> Optional<T> byStoreClass(Class<?> c) {
|
||||
if (ALL == null) {
|
||||
throw new IllegalStateException("Not initialized");
|
||||
}
|
||||
|
||||
return (Optional<T>)
|
||||
ALL.stream().filter(d -> d.getStoreClasses().contains(c)).findAny();
|
||||
return (T) ALL.stream().filter(d -> d.getStoreClasses().contains(store.getClass())).findAny().orElseThrow(() -> new IllegalArgumentException("Unknown store class"));
|
||||
}
|
||||
|
||||
public static List<DataStoreProvider> getAll() {
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package io.xpipe.app.storage;
|
||||
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public class DataStorageParser {
|
||||
|
||||
public static DataStore storeFromNode(JsonNode node) {
|
||||
var mapper = JacksonMapper.getDefault();
|
||||
try {
|
||||
return mapper.treeToValue(node, DataStore.class);
|
||||
} catch (Throwable e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package io.xpipe.app.storage;
|
||||
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public class DataStorageWriter {
|
||||
|
||||
public static JsonNode storeToNode(DataStore store) {
|
||||
var mapper = JacksonMapper.getDefault();
|
||||
return mapper.valueToTree(store);
|
||||
}
|
||||
}
|
|
@ -82,6 +82,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
String name,
|
||||
Instant lastUsed,
|
||||
Instant lastModified,
|
||||
DataStore store,
|
||||
JsonNode storeNode,
|
||||
boolean dirty,
|
||||
Validity validity,
|
||||
|
@ -93,7 +94,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
Order explicitOrder) {
|
||||
super(directory, uuid, name, lastUsed, lastModified, dirty);
|
||||
this.categoryUuid = categoryUuid;
|
||||
this.store = DataStorageParser.storeFromNode(storeNode);
|
||||
this.store = store;
|
||||
this.storeNode = storeNode;
|
||||
this.validity = validity;
|
||||
this.configuration = configuration;
|
||||
|
@ -101,7 +102,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
this.color = color;
|
||||
this.explicitOrder = explicitOrder;
|
||||
this.provider = store != null
|
||||
? DataStoreProviders.byStoreClass(store.getClass()).orElse(null)
|
||||
? DataStoreProviders.byStore(store)
|
||||
: null;
|
||||
this.storePersistentStateNode = storePersistentState;
|
||||
this.notes = notes;
|
||||
|
@ -149,8 +150,9 @@ public class DataStoreEntry extends StorageElement {
|
|||
@SneakyThrows
|
||||
public static DataStoreEntry createNew(
|
||||
@NonNull UUID uuid, @NonNull UUID categoryUuid, @NonNull String name, @NonNull DataStore store) {
|
||||
var node = DataStorageWriter.storeToNode(store);
|
||||
var validity = DataStorageParser.storeFromNode(node) == null
|
||||
var node = JacksonMapper.getDefault().valueToTree(store);
|
||||
var storeFromNode = JacksonMapper.getDefault().treeToValue(node, DataStore.class);
|
||||
var validity = storeFromNode == null
|
||||
? Validity.LOAD_FAILED
|
||||
: store.isComplete() ? Validity.COMPLETE : Validity.INCOMPLETE;
|
||||
var entry = new DataStoreEntry(
|
||||
|
@ -160,6 +162,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
name,
|
||||
Instant.now(),
|
||||
Instant.now(),
|
||||
storeFromNode,
|
||||
node,
|
||||
true,
|
||||
validity,
|
||||
|
@ -172,39 +175,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
return entry;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static DataStoreEntry createExisting(
|
||||
@NonNull Path directory,
|
||||
@NonNull UUID uuid,
|
||||
@NonNull UUID categoryUuid,
|
||||
@NonNull String name,
|
||||
@NonNull Instant lastUsed,
|
||||
@NonNull Instant lastModified,
|
||||
JsonNode storeNode,
|
||||
Configuration configuration,
|
||||
JsonNode storePersistentState,
|
||||
boolean expanded,
|
||||
DataStoreColor color,
|
||||
String notes,
|
||||
Order order) {
|
||||
return new DataStoreEntry(
|
||||
directory,
|
||||
uuid,
|
||||
categoryUuid,
|
||||
name,
|
||||
lastUsed,
|
||||
lastModified,
|
||||
storeNode,
|
||||
false,
|
||||
Validity.INCOMPLETE,
|
||||
configuration,
|
||||
storePersistentState,
|
||||
expanded,
|
||||
color,
|
||||
notes,
|
||||
order);
|
||||
}
|
||||
|
||||
public static Optional<DataStoreEntry> fromDirectory(Path dir) throws Exception {
|
||||
ObjectMapper mapper = JacksonMapper.getDefault();
|
||||
|
||||
|
@ -277,20 +247,19 @@ public class DataStoreEntry extends StorageElement {
|
|||
}
|
||||
|
||||
// Store loading is prone to errors.
|
||||
JsonNode storeNode = null;
|
||||
try {
|
||||
storeNode = DataStorageEncryption.readPossiblyEncryptedNode(mapper.readTree(storeFile.toFile()));
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
}
|
||||
return Optional.of(createExisting(
|
||||
JsonNode storeNode = DataStorageEncryption.readPossiblyEncryptedNode(mapper.readTree(storeFile.toFile()));
|
||||
var store = JacksonMapper.getDefault().treeToValue(storeNode, DataStore.class);
|
||||
return Optional.of(new DataStoreEntry(
|
||||
dir,
|
||||
uuid,
|
||||
categoryUuid,
|
||||
name,
|
||||
lastUsed,
|
||||
lastModified,
|
||||
store,
|
||||
storeNode,
|
||||
false,
|
||||
Validity.INCOMPLETE,
|
||||
configuration,
|
||||
persistentState,
|
||||
expanded,
|
||||
|
@ -482,7 +451,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
}
|
||||
|
||||
this.store = store;
|
||||
this.storeNode = DataStorageWriter.storeToNode(store);
|
||||
this.storeNode = JacksonMapper.getDefault().valueToTree(store);
|
||||
if (updateTime) {
|
||||
lastModified = Instant.now();
|
||||
}
|
||||
|
@ -491,7 +460,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
}
|
||||
|
||||
public void reassignStore() {
|
||||
this.storeNode = DataStorageWriter.storeToNode(store);
|
||||
this.storeNode = JacksonMapper.getDefault().valueToTree(store);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
@ -528,7 +497,14 @@ public class DataStoreEntry extends StorageElement {
|
|||
return;
|
||||
}
|
||||
|
||||
var newStore = DataStorageParser.storeFromNode(storeNode);
|
||||
DataStore newStore;
|
||||
try {
|
||||
newStore = JacksonMapper.getDefault().treeToValue(storeNode, DataStore.class);
|
||||
} catch (Throwable e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
newStore = null;
|
||||
}
|
||||
|
||||
if (newStore == null) {
|
||||
store = null;
|
||||
validity = Validity.LOAD_FAILED;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.app.storage;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import io.xpipe.app.ext.DataStorageExtensionProvider;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
|
@ -7,6 +8,7 @@ import io.xpipe.app.prefs.AppPrefs;
|
|||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.store.LocalStore;
|
||||
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
|
@ -129,6 +131,16 @@ public class StandardStorage extends DataStorage {
|
|||
}
|
||||
|
||||
storeEntries.put(entry.get(), entry.get());
|
||||
} catch (JacksonException ex) {
|
||||
// Data corruption and schema changes are expected
|
||||
|
||||
// We only keep invalid entries in developer mode as there's no point in keeping them in
|
||||
// production.
|
||||
if (AppPrefs.get().isDevelopmentEnvironment()) {
|
||||
directoriesToKeep.add(path);
|
||||
}
|
||||
|
||||
ErrorEvent.fromThrowable(ex).expected().omit().build().handle();
|
||||
} catch (IOException ex) {
|
||||
// IO exceptions are not expected
|
||||
exception.set(new IOException("Unable to load data from " + path + ". Is it corrupted?", ex));
|
||||
|
@ -221,7 +233,7 @@ public class StandardStorage extends DataStorage {
|
|||
.filter(entry -> entry.getValidity() != DataStoreEntry.Validity.LOAD_FAILED)
|
||||
.forEach(entry -> {
|
||||
entry.dirty = true;
|
||||
entry.setStoreNode(DataStorageWriter.storeToNode(entry.getStore()));
|
||||
entry.setStoreNode(JacksonMapper.getDefault().valueToTree(entry.getStore()));
|
||||
});
|
||||
save(false);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
package io.xpipe.core.util;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
|
||||
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import io.xpipe.core.dialog.BaseQueryElement;
|
||||
import io.xpipe.core.dialog.BusyElement;
|
||||
import io.xpipe.core.dialog.ChoiceElement;
|
||||
|
@ -11,18 +19,7 @@ import io.xpipe.core.store.FilePath;
|
|||
import io.xpipe.core.store.LocalStore;
|
||||
import io.xpipe.core.store.StorePath;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
|
||||
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.type.ArrayType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
@ -35,8 +32,6 @@ public class CoreJacksonModule extends SimpleModule {
|
|||
context.registerSubtypes(
|
||||
new NamedType(InPlaceSecretValue.class),
|
||||
new NamedType(LocalStore.class),
|
||||
new NamedType(ArrayType.class),
|
||||
new NamedType(WildcardType.class),
|
||||
new NamedType(BaseQueryElement.class),
|
||||
new NamedType(ChoiceElement.class),
|
||||
new NamedType(BusyElement.class),
|
||||
|
|
Loading…
Reference in a new issue