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);
}
}
/**
- * 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))
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;
}
}
}
}
+ 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)) {
+ if (Files.exists(p.resolve(HeadState.HEAD_STATE))) {
+ // 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());