]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.acorn/src/org/simantics/acorn/MainState.java
Acorn: Fix WriteRunnable.runReally() and other fixes
[simantics/platform.git] / bundles / org.simantics.acorn / src / org / simantics / acorn / MainState.java
index 77335289d73b446b6f10b20a0f1a03ce930ce4f0..ec8451cca78adfef37ea4377cd1fad26e2eff89f 100644 (file)
@@ -1,69 +1,96 @@
 package org.simantics.acorn;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
+import java.io.OutputStream;
 import java.io.Serializable;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
+import java.util.function.Consumer;
 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.file.RuntimeIOException;
+import org.simantics.databoard.serialization.Serializer;
+import org.simantics.databoard.util.binary.BinaryMemory;
 import org.simantics.utils.FileUtils;
 
 public class MainState implements Serializable {
 
     private static final long serialVersionUID = 6237383147637270225L;
 
+    public static final String MAIN_STATE = "main.state";
+    
     public int headDir = 0;
 
     public MainState() {
     }
-
-    public MainState(int headDir) {
+    
+    private MainState(int headDir) {
         this.headDir = headDir;
     }
 
-    public static MainState load(Path directory) throws IOException {
+    public static MainState load(Path directory, Consumer<Exception> callback) throws IOException {
         Files.createDirectories(directory);
-        Path f = directory.resolve("main.state");
+        Path mainState = directory.resolve(MAIN_STATE);
         try {
+            byte[] bytes = Files.readAllBytes(mainState);
             MainState state = null;
-            try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(Files.newInputStream(f)))) {
-                state = (MainState) in.readObject();
+            try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
+                state = (MainState) org.simantics.databoard.Files.readFile(bais, Bindings.getBindingUnchecked(MainState.class));
             }
+            
             while (true) {
-                Path last = directory.resolve(Integer.toString(state.headDir - 1));
+                Path latest = directory.resolve(Integer.toString(state.headDir - 1));
                 try {
-                    Path headState = last.resolve("head.state");
+                    Path headState = latest.resolve(HeadState.HEAD_STATE);
                     HeadState.validateHeadStateIntegrity(headState);
                     break;
                 } catch (InvalidHeadStateException e) {
                     e.printStackTrace();
                     state.headDir--;
-                    uncheckedDeleteAll(last);
+                    callback.accept(e);
+                } finally {
+                    cleanBaseDirectory(directory, latest, callback);
                 }
             }
             return state;
-        } catch(IOException i) {
-            return new MainState( findNewHeadState(directory) );
-        } catch(ClassNotFoundException c) {
-            throw new Error("MainState class not found", c);
+        } catch(Exception i) {
+            callback.accept(i);
+            int largest = -1;
+            Path latest = findNewHeadStateDir(directory, callback);
+            if (latest != null)
+                largest = safeParseInt(-1, latest.getFileName().toString());
+            // +1 because we want to return the next head version to use,
+            // not the latest existing version.
+            largest++;
+            MainState state = new MainState( largest );
+            cleanBaseDirectory(directory, latest, callback);
+            return state;
         } finally {
-            if (Files.exists(f)) {
-                Files.delete(f);
+            if (Files.exists(mainState)) {
+                Files.delete(mainState);
             }
         }
     }
 
     public void save(Path directory) throws IOException {
-        Path f = directory.resolve("main.state");
-        try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(Files.newOutputStream(f)))) {
-            out.writeObject(this);
+        Path f = directory.resolve(MAIN_STATE);
+        BinaryMemory rf = new BinaryMemory(4096);
+        try {
+            MutableVariant v = new MutableVariant(Bindings.getBindingUnchecked(MainState.class), this);
+            Serializer s = Bindings.getSerializerUnchecked( Bindings.VARIANT );
+            s.serialize(rf, v);
+        } finally {
+            rf.close();
+        }
+        byte[] bytes = rf.toByteBuffer().array();
+        try (OutputStream out = Files.newOutputStream(f)) {
+            out.write(bytes);
         }
         FileIO.syncPath(f);
     }
@@ -78,13 +105,13 @@ public class MainState implements Serializable {
     }
 
     /**
-     * TODO> shouldn't do two things in the same function, this does both head.state search and directory cleanup
      *  
      * @param directory
+     * @param callback 
      * @return
      * @throws IOException
      */
-    private static int findNewHeadState(Path directory) throws IOException {
+    private static Path findNewHeadStateDir(Path directory, Consumer<Exception> callback) throws IOException {
         try (Stream<Path> s = Files.walk(directory, 1)) {
             List<Path> reverseSortedPaths = s
             .filter(p -> !p.equals(directory) && isInteger(p) && Files.isDirectory(p))
@@ -94,25 +121,19 @@ public class MainState implements Serializable {
                 return Integer.compare(p2Name, p1Name);
             }).collect(Collectors.toList());
 
-            int largest = -1;
+            Path latest = null;
             for (Path last : reverseSortedPaths) {
-                Path headState = last.resolve("head.state");
-                if (Files.exists(headState)) {
-                    try {
-                        HeadState.validateHeadStateIntegrity(headState);
-                        largest = safeParseInt(-1, last.getFileName().toString());
-                        break;
-                    } catch (IOException | InvalidHeadStateException e) {
-                        e.printStackTrace();
-                        uncheckedDeleteAll(last);
-                    }
-                } else {
-                    uncheckedDeleteAll(last);
+                Path headState = last.resolve(HeadState.HEAD_STATE);
+                try {
+                    HeadState.validateHeadStateIntegrity(headState);
+                    latest = last;
+                    break;
+                } catch (IOException | InvalidHeadStateException e) {
+                    // Cleanup is done in {@link cleanBaseDirectory} method
+                    callback.accept(e);
                 }
             }
-            // +1 because we want to return the next head version to use,
-            // not the latest existing version.
-            return largest + 1;
+            return latest;
         }
     }
 
@@ -124,6 +145,30 @@ public class MainState implements Serializable {
         }
     }
 
+    private static void cleanBaseDirectory(Path directory, Path latest, Consumer<Exception> callback) throws IOException {
+        try (Stream<Path> s = Files.walk(directory, 1)) {
+            List<Path> reverseSortedPaths = s
+            .filter(p -> !p.equals(directory) && isInteger(p) && Files.isDirectory(p))
+            .sorted((p1, p2) -> {
+                int p1Name = Integer.parseInt(p1.getFileName().toString()); 
+                int p2Name = Integer.parseInt(p2.getFileName().toString());
+                return Integer.compare(p2Name, p1Name);
+            }).collect(Collectors.toList());
+            
+            for (Path p : reverseSortedPaths) {
+                if (!p.equals(latest)) {
+                    // this indicates that there is a possibility that index and vg's are out of sync
+                    // if we are able to find folders with higher number than the current head.state
+                    callback.accept(null);
+                    uncheckedDeleteAll(p);
+                } else {
+                    break;
+                }
+            }
+            
+        }
+    }
+
     private static void uncheckedDeleteAll(Path path) {
         try {
             FileUtils.deleteAll(path.toFile());