From: jsimomaa Date: Mon, 5 Sep 2016 12:28:00 +0000 (+0300) Subject: Some Acorn improvements X-Git-Tag: v1.25.0~135^2 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F48%2F48%2F3;p=simantics%2Fplatform.git Some Acorn improvements 1) Acorn: do not make snapshot if cancelCommit() is called In some situations it is possible that when cancelCommit() is called that acorn head.state writing writes nonsense due to IllegalStateException being thrown from LRUObject.getStateKey() 2) Acorn: use a lock file to lock the current database Use a lock file to prevent simultaneous usages of same database in Acorn refs #6596 Change-Id: Ia33b90f85371c48154763f8a603cdce9a61d9ed6 --- diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornDriver.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornDriver.java index 0e6d52b9a..99ec49062 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornDriver.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornDriver.java @@ -65,38 +65,7 @@ public class AcornDriver implements Driver { @Override public ServerI getServer(String address, Properties properties) throws DatabaseException { - return new ServerI() { - - @Override - public void stop() throws DatabaseException { - // nop - } - - @Override - public void start() throws DatabaseException { - // nop - } - - @Override - public boolean isActive() throws DatabaseException { - return true; - } - - @Override - public String getAddress() throws DatabaseException { - return address; - } - - @Override - public String executeAndDisconnect(String command) throws DatabaseException { - return ""; - } - - @Override - public String execute(String command) throws DatabaseException { - return ""; - } - }; + return new AcornServerI(address); } @Override @@ -104,5 +73,44 @@ public class AcornDriver implements Driver { Path dbFolder = Paths.get(address); return new AcornManagement(dbFolder, properties); } + + private static class AcornServerI implements ServerI { + + private String address; + + public AcornServerI(String address) { + this.address = address; + } + + @Override + public void stop() throws DatabaseException { + AcornDatabaseManager.getDatabase(Paths.get(address)).tryToStop(); + } + + @Override + public void start() throws DatabaseException { + AcornDatabaseManager.getDatabase(Paths.get(address)).start(); + } + + @Override + public boolean isActive() throws DatabaseException { + return AcornDatabaseManager.getDatabase(Paths.get(address)).isRunning(); + } + + @Override + public String getAddress() throws DatabaseException { + return address; + } + + @Override + public String executeAndDisconnect(String command) throws DatabaseException { + return ""; + } + + @Override + public String execute(String command) throws DatabaseException { + return ""; + } + } } diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java index 561a03913..a7bccf087 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java @@ -34,7 +34,7 @@ public class AcornManagement implements Management { public void create() throws DatabaseException { db.initFolder(properties); if (!exist()) - throw new DatabaseException("Failed to create ProCore database. folder=" + db.getFolder()); + throw new DatabaseException("Failed to create Acorn database. folder=" + db.getFolder()); } @Override diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java index 2796e3e02..774b60554 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java @@ -140,7 +140,8 @@ public class GraphClientImpl2 implements Database.Session { } public void makeSnapshot(boolean force) throws IOException { - clusters.makeSnapshot(locator, force); + if (safeToMakeSnapshot) + clusters.makeSnapshot(locator, force); } public T clone(ClusterUID uid, ClusterCreator creator) throws DatabaseException { @@ -166,6 +167,8 @@ public class GraphClientImpl2 implements Database.Session { private boolean closed = false; private boolean isClosing = false; + // Add check to make sure if it safe to make snapshot (used with cancel which is not yet supported and may cause corrupted head.state writing) + private boolean safeToMakeSnapshot = true; @Override public void close() throws ProCoreException { @@ -173,7 +176,7 @@ public class GraphClientImpl2 implements Database.Session { if(!closed && !isClosing) { isClosing = true; try { - makeSnapshot(true); + makeSnapshot(true); mainProgram.close(); clusters.shutdown(); @@ -228,15 +231,17 @@ public class GraphClientImpl2 implements Database.Session { public long cancelCommit(long transactionId, long changeSetId, byte[] metadata, OnChangeSetUpdate onChangeSetUpdate) throws ProCoreException { - System.err.println("GraphClientImpl2.cancelCommit() called!! this is experimental and might cause havoc!"); - try { - undo(new long[] {changeSetId}, onChangeSetUpdate); - } catch (SDBException e) { - e.printStackTrace(); - throw new ProCoreException(e); - } - clusters.state.headChangeSetId++; - return clusters.state.headChangeSetId; + safeToMakeSnapshot = false; + throw new UnsupportedOperationException("org.simantics.acorn.GraphClientImpl2.cancelCommit() is not supported operation! Closing down to prevent further havoc"); +// System.err.println("GraphClientImpl2.cancelCommit() called!! this is experimental and might cause havoc!"); +// try { +// undo(new long[] {changeSetId}, onChangeSetUpdate); +// } catch (SDBException e) { +// e.printStackTrace(); +// throw new ProCoreException(e); +// } +// clusters.state.headChangeSetId++; +// return clusters.state.headChangeSetId; } @Override diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java index 63c73dd63..be505c603 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java @@ -2,6 +2,8 @@ package org.simantics.acorn.internal; import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileLock; import java.nio.file.DirectoryStream; import java.nio.file.FileVisitOption; import java.nio.file.FileVisitResult; @@ -28,6 +30,12 @@ public class AcornDatabase implements Database { private DatabaseUserAgent userAgent; + private RandomAccessFile raLockFile; + + private FileLock lock; + + private boolean isRunning; + public AcornDatabase(Path folder) { this.folder = folder; } @@ -93,23 +101,49 @@ public class AcornDatabase implements Database { @Override public void deleteFiles() throws ProCoreException { - // TODO: somehow check that the acorn client is not active. deleteTree(folder); } @Override public void start() throws ProCoreException { + Path lockFile = folder.resolve("lock"); + try { + if (!Files.exists(lockFile)) + Files.createFile(lockFile); + + raLockFile = new RandomAccessFile(lockFile.toFile(), "rw"); + lock = raLockFile.getChannel().tryLock(); + if (lock == null) { + throw new ProCoreException("The database in folder " + folder.toAbsolutePath() + " is already in use!"); + } + + isRunning = true; + + } catch (IOException e) { + e.printStackTrace(); + } } @Override public boolean isRunning() throws ProCoreException { - return true; + return isRunning; } @Override public boolean tryToStop() throws ProCoreException { + try { + lock.release(); + raLockFile.close(); + + Files.deleteIfExists(folder.resolve("lock")); + + isRunning = false; + + } catch (IOException e) { + e.printStackTrace(); + } + return true; -// throw new UnsupportedOperationException(); } @Override @@ -118,7 +152,7 @@ public class AcornDatabase implements Database { @Override public boolean isConnected() throws ProCoreException { - return true; + return isRunning; } @Override