fd38bc98542052bbdb2e762c65d49a4cad4f3585
[simantics/platform.git] / bundles / org.simantics.acorn / src / org / simantics / acorn / HeadState.java
1 package org.simantics.acorn;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.IOException;
5 import java.io.OutputStream;
6 import java.nio.file.Files;
7 import java.nio.file.Path;
8 import java.security.MessageDigest;
9 import java.security.NoSuchAlgorithmException;
10 import java.util.ArrayList;
11 import java.util.Arrays;
12
13 import org.simantics.acorn.exception.InvalidHeadStateException;
14 import org.simantics.databoard.Bindings;
15 import org.simantics.databoard.adapter.AdapterConstructionException;
16 import org.simantics.databoard.binding.mutable.MutableVariant;
17 import org.simantics.databoard.serialization.Serializer;
18 import org.simantics.databoard.util.binary.BinaryMemory;
19
20 public class HeadState {
21
22     public static final String HEAD_STATE = "head.state";
23     public static final String SHA_1 = "SHA-1";
24     
25     public int headChangeSetId = 0;
26     public long transactionId = 1;
27     public long reservedIds = 3;
28
29     public ArrayList<String> clusters = new ArrayList<>();
30     public ArrayList<String> files = new ArrayList<>();
31     public ArrayList<String> stream = new ArrayList<>();
32     public ArrayList<String> cs = new ArrayList<>();
33 //    public ArrayList<String> ccs = new ArrayList<String>();
34
35     public long tailChangeSetId = 1;
36
37     public static HeadState load(Path directory) throws InvalidHeadStateException {
38         Path f = directory.resolve(HEAD_STATE);
39         
40         try {
41             byte[] bytes = Files.readAllBytes(f);
42             MessageDigest sha1 = MessageDigest.getInstance(SHA_1);
43             int digestLength = sha1.getDigestLength();
44             
45             sha1.update(bytes, digestLength, bytes.length - digestLength);
46             byte[] newChecksum = sha1.digest();
47             if (!Arrays.equals(newChecksum, Arrays.copyOfRange(bytes, 0, digestLength))) {
48                 throw new InvalidHeadStateException(
49                         "Checksum " + Arrays.toString(newChecksum) + " does not match excpected "
50                                 + Arrays.toString(Arrays.copyOfRange(bytes, 0, digestLength)) + " for " + f.toAbsolutePath());
51             }
52             try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes, digestLength, bytes.length - digestLength)) {
53                 HeadState object = (HeadState) org.simantics.databoard.Files.readFile(bais, Bindings.getBindingUnchecked(HeadState.class));
54                 return object;
55             }
56         } catch (IOException i) {
57                 Throwable cause = i.getCause();
58                 if(cause instanceof AdapterConstructionException) {
59                         HeadState1 old = HeadState1.load(directory);
60                         return old.migrate();
61                 }
62             return new HeadState();
63 //            throw new InvalidHeadStateException(i);
64         } catch (NoSuchAlgorithmException e) {
65             throw new Error("SHA-1 Algorithm not found", e);
66         } catch (Throwable t) {
67             throw new InvalidHeadStateException(t);
68         }
69     }
70     
71     public void save(Path directory) throws IOException {
72         Path f = directory.resolve(HEAD_STATE);
73         try {
74             BinaryMemory rf = new BinaryMemory(4096);
75             try {
76                 MutableVariant v = new MutableVariant(Bindings.getBindingUnchecked(HeadState.class), this);
77                 Serializer s = Bindings.getSerializerUnchecked( Bindings.VARIANT );
78                 s.serialize(rf, v);
79             } finally {
80                 rf.close();
81             }
82             
83             byte[] bytes = rf.toByteBuffer().array();
84             
85             MessageDigest sha1 = MessageDigest.getInstance(SHA_1);
86             sha1.update(bytes);
87             byte[] checksum = sha1.digest();
88             
89             try (OutputStream out = Files.newOutputStream(f)) {
90                 out.write(checksum);
91                 out.write(bytes);
92             }
93             FileIO.syncPath(f);
94         } catch (NoSuchAlgorithmException e) {
95             throw new Error("SHA-1 digest not found, should not happen", e);
96         }
97     }
98
99     public static void validateHeadStateIntegrity(Path headState) throws InvalidHeadStateException, IOException {
100         try {
101             byte[] bytes = Files.readAllBytes(headState);
102             MessageDigest sha1 = MessageDigest.getInstance(SHA_1);
103             int digestLength = sha1.getDigestLength();
104             sha1.update(bytes, digestLength, bytes.length - digestLength);
105             byte[] newChecksum = sha1.digest();
106             if (!Arrays.equals(newChecksum, Arrays.copyOfRange(bytes, 0, digestLength))) {
107                 throw new InvalidHeadStateException(
108                         "Checksum " + Arrays.toString(newChecksum) + " does not match excpected "
109                                 + Arrays.toString(Arrays.copyOfRange(bytes, 0, digestLength)) + " for " + headState.toAbsolutePath());
110             }
111         } catch (NoSuchAlgorithmException e) {
112             throw new Error("SHA-1 digest not found, should not happen", e);
113         }
114     }
115 }