import org.simantics.databoard.binding.mutable.MutableVariant;
import org.simantics.databoard.serialization.Serializer;
import org.simantics.databoard.util.binary.BinaryMemory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class HeadState {
+ private static transient final Logger LOGGER = LoggerFactory.getLogger(HeadState.class);
+
public static final String HEAD_STATE = "head.state";
public static final String SHA_1 = "SHA-1";
byte[] bytes = Files.readAllBytes(f);
MessageDigest sha1 = MessageDigest.getInstance(SHA_1);
int digestLength = sha1.getDigestLength();
-
sha1.update(bytes, digestLength, bytes.length - digestLength);
byte[] newChecksum = sha1.digest();
if (!Arrays.equals(newChecksum, Arrays.copyOfRange(bytes, 0, digestLength))) {
}
}
- public static void validateHeadStateIntegrity(Path headState) throws InvalidHeadStateException, IOException {
+ public static boolean validateHeadStateIntegrity(Path headState) {
try {
byte[] bytes = Files.readAllBytes(headState);
MessageDigest sha1 = MessageDigest.getInstance(SHA_1);
sha1.update(bytes, digestLength, bytes.length - digestLength);
byte[] newChecksum = sha1.digest();
if (!Arrays.equals(newChecksum, Arrays.copyOfRange(bytes, 0, digestLength))) {
- throw new InvalidHeadStateException(
- "Checksum " + Arrays.toString(newChecksum) + " does not match excpected "
- + Arrays.toString(Arrays.copyOfRange(bytes, 0, digestLength)) + " for " + headState.toAbsolutePath());
+ LOGGER.error("Checksum " + Arrays.toString(newChecksum) + " does not match excpected " + Arrays.toString(Arrays.copyOfRange(bytes, 0, digestLength)) + " for " + headState.toAbsolutePath());
+ return false;
}
+ return true;
+ } catch (IOException e) {
+ LOGGER.error("An I/O error occured while validating integrity of head.state", e);
+ return false;
} catch (NoSuchAlgorithmException e) {
+ LOGGER.error("SHA-1 digest not found, should not happen", e);
throw new Error("SHA-1 digest not found, should not happen", e);
}
}
+
}
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import org.simantics.acorn.exception.InvalidHeadStateException;
import org.simantics.databoard.Bindings;
import org.simantics.databoard.binding.mutable.MutableVariant;
import org.simantics.databoard.util.binary.BinaryMemory;
Bindings.getBindingUnchecked(MainState.class));
int latestRevision = state.headDir - 1;
try {
- HeadState.validateHeadStateIntegrity(directory.resolve(latestRevision + "/" + HeadState.HEAD_STATE));
- archiveRevisionDirectories(directory, latestRevision, rollbackCallback);
- return state;
- } catch (InvalidHeadStateException e) {
+ if (HeadState.validateHeadStateIntegrity(directory.resolve(latestRevision + "/" + HeadState.HEAD_STATE))) {
+ archiveRevisionDirectories(directory, latestRevision, rollbackCallback);
+ return state;
+ }
LOGGER.warn("Failed to start database from revision " + latestRevision + " stored in " + mainState + ". " + HeadState.HEAD_STATE + " is invalid.");
return rollback(directory, rollbackCallback);
} catch (FileNotFoundException e) {
}
} catch (FileNotFoundException e) {
// The database may also be totally empty at this point
- if (!listRevisionDirs(directory, true, MainState::isInteger).isEmpty())
+ if (listRevisionDirs(directory, true, MainState::isInteger).isEmpty())
return new MainState(0);
LOGGER.warn("Unclean exit detected, " + mainState + " not found. Initiating automatic rollback.");
private static MainState rollback(Path directory, Runnable rollbackCallback) throws IOException {
LOGGER.warn("Database rollback initiated for " + directory);
rollbackCallback.run();
- Path latest = findNewHeadStateDir(directory, rollbackCallback);
+ Path latest = findNewHeadStateDir(directory);
int latestRevision = latest != null ? safeParseInt(-1, latest) : -1;
// +1 because we want to return the next head version to use,
// not the latest existing version.
* @return
* @throws IOException
*/
- private static Path findNewHeadStateDir(Path directory, Runnable rollbackCallback) throws IOException {
- for (Path last : listRevisionDirs(directory, true, MainState::isInteger)) {
- try {
- HeadState.validateHeadStateIntegrity(last.resolve(HeadState.HEAD_STATE));
+ private static Path findNewHeadStateDir(Path directory) throws IOException {
+ for (Path last : listRevisionDirs(directory, true, MainState::isInteger))
+ if (HeadState.validateHeadStateIntegrity(last.resolve(HeadState.HEAD_STATE)))
return last;
- } catch (IOException | InvalidHeadStateException e) {
- // Cleanup is done in {@link cleanRevisionDirectories} method
- rollbackCallback.run();
- }
- }
return null;
}