package org.simantics.acorn; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import org.simantics.acorn.exception.InvalidHeadStateException; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.util.binary.BinaryMemory; public class HeadState { public static final String HEAD_STATE = "head.state"; public static final String SHA_1 = "SHA-1"; public int headChangeSetId = 0; public long transactionId = 1; public long reservedIds = 3; public ArrayList clusters = new ArrayList<>(); public ArrayList files = new ArrayList<>(); public ArrayList stream = new ArrayList<>(); public ArrayList cs = new ArrayList<>(); // public ArrayList ccs = new ArrayList(); public static HeadState load(Path directory) throws InvalidHeadStateException { Path f = directory.resolve(HEAD_STATE); try { 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))) { throw new InvalidHeadStateException( "Checksum " + Arrays.toString(newChecksum) + " does not match excpected " + Arrays.toString(Arrays.copyOfRange(bytes, 0, digestLength)) + " for " + f.toAbsolutePath()); } try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes, digestLength, bytes.length - digestLength)) { HeadState object = (HeadState) org.simantics.databoard.Files.readFile(bais, Bindings.getBindingUnchecked(HeadState.class)); return object; } } catch (IOException i) { return new HeadState(); // throw new InvalidHeadStateException(i); } catch (NoSuchAlgorithmException e) { throw new Error("SHA-1 Algorithm not found", e); } catch (Throwable t) { throw new InvalidHeadStateException(t); } } public void save(Path directory) throws IOException { Path f = directory.resolve(HEAD_STATE); try { BinaryMemory rf = new BinaryMemory(4096); try { MutableVariant v = new MutableVariant(Bindings.getBindingUnchecked(HeadState.class), this); Serializer s = Bindings.getSerializerUnchecked( Bindings.VARIANT ); s.serialize(rf, v); } finally { rf.close(); } byte[] bytes = rf.toByteBuffer().array(); MessageDigest sha1 = MessageDigest.getInstance(SHA_1); sha1.update(bytes); byte[] checksum = sha1.digest(); try (OutputStream out = Files.newOutputStream(f)) { out.write(checksum); out.write(bytes); } FileIO.syncPath(f); } catch (NoSuchAlgorithmException e) { throw new Error("SHA-1 digest not found, should not happen", e); } } public static void validateHeadStateIntegrity(Path headState) throws InvalidHeadStateException, IOException { try { byte[] bytes = Files.readAllBytes(headState); 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))) { throw new InvalidHeadStateException( "Checksum " + Arrays.toString(newChecksum) + " does not match excpected " + Arrays.toString(Arrays.copyOfRange(bytes, 0, digestLength)) + " for " + headState.toAbsolutePath()); } } catch (NoSuchAlgorithmException e) { throw new Error("SHA-1 digest not found, should not happen", e); } } }