More error fixes

This commit is contained in:
crschnick 2024-07-04 14:06:50 +00:00
parent 32c3f42aaa
commit 50f8e40d99
6 changed files with 45 additions and 102 deletions

View file

@ -64,17 +64,11 @@ public class DataStoreProviders {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends DataStoreProvider> T byStore(DataStore store) { 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) { if (ALL == null) {
throw new IllegalStateException("Not initialized"); throw new IllegalStateException("Not initialized");
} }
return (Optional<T>) return (T) ALL.stream().filter(d -> d.getStoreClasses().contains(store.getClass())).findAny().orElseThrow(() -> new IllegalArgumentException("Unknown store class"));
ALL.stream().filter(d -> d.getStoreClasses().contains(c)).findAny();
} }
public static List<DataStoreProvider> getAll() { public static List<DataStoreProvider> getAll() {

View file

@ -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;
}
}
}

View file

@ -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);
}
}

View file

@ -82,6 +82,7 @@ public class DataStoreEntry extends StorageElement {
String name, String name,
Instant lastUsed, Instant lastUsed,
Instant lastModified, Instant lastModified,
DataStore store,
JsonNode storeNode, JsonNode storeNode,
boolean dirty, boolean dirty,
Validity validity, Validity validity,
@ -93,7 +94,7 @@ public class DataStoreEntry extends StorageElement {
Order explicitOrder) { Order explicitOrder) {
super(directory, uuid, name, lastUsed, lastModified, dirty); super(directory, uuid, name, lastUsed, lastModified, dirty);
this.categoryUuid = categoryUuid; this.categoryUuid = categoryUuid;
this.store = DataStorageParser.storeFromNode(storeNode); this.store = store;
this.storeNode = storeNode; this.storeNode = storeNode;
this.validity = validity; this.validity = validity;
this.configuration = configuration; this.configuration = configuration;
@ -101,7 +102,7 @@ public class DataStoreEntry extends StorageElement {
this.color = color; this.color = color;
this.explicitOrder = explicitOrder; this.explicitOrder = explicitOrder;
this.provider = store != null this.provider = store != null
? DataStoreProviders.byStoreClass(store.getClass()).orElse(null) ? DataStoreProviders.byStore(store)
: null; : null;
this.storePersistentStateNode = storePersistentState; this.storePersistentStateNode = storePersistentState;
this.notes = notes; this.notes = notes;
@ -149,8 +150,9 @@ public class DataStoreEntry extends StorageElement {
@SneakyThrows @SneakyThrows
public static DataStoreEntry createNew( public static DataStoreEntry createNew(
@NonNull UUID uuid, @NonNull UUID categoryUuid, @NonNull String name, @NonNull DataStore store) { @NonNull UUID uuid, @NonNull UUID categoryUuid, @NonNull String name, @NonNull DataStore store) {
var node = DataStorageWriter.storeToNode(store); var node = JacksonMapper.getDefault().valueToTree(store);
var validity = DataStorageParser.storeFromNode(node) == null var storeFromNode = JacksonMapper.getDefault().treeToValue(node, DataStore.class);
var validity = storeFromNode == null
? Validity.LOAD_FAILED ? Validity.LOAD_FAILED
: store.isComplete() ? Validity.COMPLETE : Validity.INCOMPLETE; : store.isComplete() ? Validity.COMPLETE : Validity.INCOMPLETE;
var entry = new DataStoreEntry( var entry = new DataStoreEntry(
@ -160,6 +162,7 @@ public class DataStoreEntry extends StorageElement {
name, name,
Instant.now(), Instant.now(),
Instant.now(), Instant.now(),
storeFromNode,
node, node,
true, true,
validity, validity,
@ -172,39 +175,6 @@ public class DataStoreEntry extends StorageElement {
return entry; 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 { public static Optional<DataStoreEntry> fromDirectory(Path dir) throws Exception {
ObjectMapper mapper = JacksonMapper.getDefault(); ObjectMapper mapper = JacksonMapper.getDefault();
@ -277,20 +247,19 @@ public class DataStoreEntry extends StorageElement {
} }
// Store loading is prone to errors. // Store loading is prone to errors.
JsonNode storeNode = null; JsonNode storeNode = DataStorageEncryption.readPossiblyEncryptedNode(mapper.readTree(storeFile.toFile()));
try { var store = JacksonMapper.getDefault().treeToValue(storeNode, DataStore.class);
storeNode = DataStorageEncryption.readPossiblyEncryptedNode(mapper.readTree(storeFile.toFile())); return Optional.of(new DataStoreEntry(
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
}
return Optional.of(createExisting(
dir, dir,
uuid, uuid,
categoryUuid, categoryUuid,
name, name,
lastUsed, lastUsed,
lastModified, lastModified,
store,
storeNode, storeNode,
false,
Validity.INCOMPLETE,
configuration, configuration,
persistentState, persistentState,
expanded, expanded,
@ -482,7 +451,7 @@ public class DataStoreEntry extends StorageElement {
} }
this.store = store; this.store = store;
this.storeNode = DataStorageWriter.storeToNode(store); this.storeNode = JacksonMapper.getDefault().valueToTree(store);
if (updateTime) { if (updateTime) {
lastModified = Instant.now(); lastModified = Instant.now();
} }
@ -491,7 +460,7 @@ public class DataStoreEntry extends StorageElement {
} }
public void reassignStore() { public void reassignStore() {
this.storeNode = DataStorageWriter.storeToNode(store); this.storeNode = JacksonMapper.getDefault().valueToTree(store);
dirty = true; dirty = true;
} }
@ -528,7 +497,14 @@ public class DataStoreEntry extends StorageElement {
return; 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) { if (newStore == null) {
store = null; store = null;
validity = Validity.LOAD_FAILED; validity = Validity.LOAD_FAILED;

View file

@ -1,5 +1,6 @@
package io.xpipe.app.storage; package io.xpipe.app.storage;
import com.fasterxml.jackson.core.JacksonException;
import io.xpipe.app.ext.DataStorageExtensionProvider; import io.xpipe.app.ext.DataStorageExtensionProvider;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent; 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.process.OsType;
import io.xpipe.core.store.LocalStore; import io.xpipe.core.store.LocalStore;
import io.xpipe.core.util.JacksonMapper;
import lombok.Getter; import lombok.Getter;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
@ -129,6 +131,16 @@ public class StandardStorage extends DataStorage {
} }
storeEntries.put(entry.get(), entry.get()); 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) { } catch (IOException ex) {
// IO exceptions are not expected // IO exceptions are not expected
exception.set(new IOException("Unable to load data from " + path + ". Is it corrupted?", ex)); 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) .filter(entry -> entry.getValidity() != DataStoreEntry.Validity.LOAD_FAILED)
.forEach(entry -> { .forEach(entry -> {
entry.dirty = true; entry.dirty = true;
entry.setStoreNode(DataStorageWriter.storeToNode(entry.getStore())); entry.setStoreNode(JacksonMapper.getDefault().valueToTree(entry.getStore()));
}); });
save(false); save(false);
} }

View file

@ -1,5 +1,13 @@
package io.xpipe.core.util; 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.BaseQueryElement;
import io.xpipe.core.dialog.BusyElement; import io.xpipe.core.dialog.BusyElement;
import io.xpipe.core.dialog.ChoiceElement; 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.LocalStore;
import io.xpipe.core.store.StorePath; 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.io.IOException;
import java.lang.reflect.WildcardType;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
@ -35,8 +32,6 @@ public class CoreJacksonModule extends SimpleModule {
context.registerSubtypes( context.registerSubtypes(
new NamedType(InPlaceSecretValue.class), new NamedType(InPlaceSecretValue.class),
new NamedType(LocalStore.class), new NamedType(LocalStore.class),
new NamedType(ArrayType.class),
new NamedType(WildcardType.class),
new NamedType(BaseQueryElement.class), new NamedType(BaseQueryElement.class),
new NamedType(ChoiceElement.class), new NamedType(ChoiceElement.class),
new NamedType(BusyElement.class), new NamedType(BusyElement.class),