import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Properties;
+import org.simantics.db.Database;
import org.simantics.db.DatabaseUserAgent;
import org.simantics.db.Driver;
import org.simantics.db.ServerI;
public static final String AcornDriverName = "acorn";
+ private Map<String, ServerI> servers = new HashMap<>();
+ private Map<String, Management> managements = new HashMap<>();
+
@Override
public String getName() {
return AcornDriverName;
@Override
public DatabaseUserAgent getDatabaseUserAgent(String address) throws DatabaseException {
- Path dbFolder = Paths.get(address);
- return AcornDatabaseManager.getDatabase(dbFolder).getUserAgent();
+ return AcornDatabaseManager.getDatabase(Paths.get(address)).getUserAgent();
}
@Override
public void setDatabaseUserAgent(String address, DatabaseUserAgent dbUserAgent) throws DatabaseException {
- Path dbFolder = Paths.get(address);
- AcornDatabaseManager.getDatabase(dbFolder).setUserAgent(dbUserAgent);
+ AcornDatabaseManager.getDatabase(Paths.get(address)).setUserAgent(dbUserAgent);
}
@Override
}
}, null);
if (!properties.containsKey("clientId"))
- properties.put("clientId", dbFolder.toFile().getAbsolutePath());
+ properties.put("clientId", dbFolder.toAbsolutePath().toString());
session.registerService(Properties.class, properties);
Session s = session.peekService(Session.class);
if (null == s)
@Override
public ServerI getServer(String address, Properties properties) throws DatabaseException {
- return new AcornServerI(address);
+ ServerI server = servers.get(address);
+ if (server == null) {
+ server = new AcornServerI(AcornDatabaseManager.getDatabase(Paths.get(address)), address);
+ servers.put(address, server);
+ }
+ return server;
}
@Override
public Management getManagement(String address, Properties properties) throws DatabaseException {
- Path dbFolder = Paths.get(address);
- return new AcornManagement(dbFolder, properties);
+ Management mgmt = managements.get(address);
+ if (mgmt == null) {
+ mgmt = new AcornManagement(AcornDatabaseManager.getDatabase(Paths.get(address)), properties);
+ managements.put(address, mgmt);
+ }
+ return mgmt;
}
private static class AcornServerI implements ServerI {
+ private Database database;
private String address;
- public AcornServerI(String address) {
+ public AcornServerI(Database db, String address) {
+ this.database = db;
this.address = address;
}
@Override
public void stop() throws DatabaseException {
- AcornDatabaseManager.getDatabase(Paths.get(address)).tryToStop();
+ database.tryToStop();
}
@Override
public void start() throws DatabaseException {
- AcornDatabaseManager.getDatabase(Paths.get(address)).start();
+ database.start();
}
@Override
public boolean isActive() throws DatabaseException {
- return AcornDatabaseManager.getDatabase(Paths.get(address)).isRunning();
+ return database.isRunning();
}
@Override
private final Database db;
private final Properties properties;
- AcornManagement(Path dbFolder, Properties properties) throws ProCoreException {
- db = AcornDatabaseManager.getDatabase(dbFolder);
+ AcornManagement(Database db, Properties properties) throws ProCoreException {
+ this.db = db;
this.properties = properties;
}
import org.simantics.db.event.SessionEvent;
import org.simantics.db.event.SessionListener;
import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.RuntimeDatabaseException;
import org.simantics.db.service.LifecycleSupport;
import org.simantics.utils.datastructures.ListenerList;
private static AcornSessionManagerImpl INSTANCE;
- private ConcurrentHashMap<SessionImplSocket, SessionImplSocket> sessionMap = new ConcurrentHashMap<SessionImplSocket, SessionImplSocket>();
- private ListenerList<SessionListener> sessionListeners = new ListenerList<SessionListener>(SessionListener.class);
+ private ConcurrentHashMap<SessionImplDb, Database.Session> sessionMap = new ConcurrentHashMap<>();
+ private ListenerList<SessionListener> sessionListeners = new ListenerList<>(SessionListener.class);
private SessionErrorHandler errorHandler;
private Database database;
database = AcornDatabaseManager.getDatabase(dbFolder);
Database.Session dbSession = database.newSession(sessionImpl);
sessionImpl.connect(sessionReference, dbSession);
- sessionMap.put(sessionImpl, sessionImpl);
+ sessionMap.put(sessionImpl, dbSession);
fireSessionOpened(sessionImpl);
ok = true;
} catch (Throwable e) {
@Override
public void shutdown(Session s, Throwable cause) {
- SessionImplSocket sis = sessionMap.get(s);
+ SessionImplSocket sis = (SessionImplSocket) s;
if (null == sis)
return;
try {
return database;
}
+ public GraphClientImpl2 getClient() {
+ if (sessionMap.values().size() > 1)
+ throw new RuntimeDatabaseException("Currently only one GraphClientImpl2 per session is supported!");
+ org.simantics.db.Database.Session client = sessionMap.values().iterator().next();
+ return (GraphClientImpl2) client;
+ }
}
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.simantics.acorn.cluster.ClusterImpl;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
+import org.simantics.acorn.exception.InvalidHeadStateException;
import org.simantics.acorn.internal.ClusterSupport2;
import org.simantics.acorn.lru.ChangeSetInfo;
import org.simantics.acorn.lru.ClusterInfo;
import org.simantics.acorn.lru.FileInfo;
import org.simantics.acorn.lru.LRU;
import org.simantics.db.ClusterCreator;
-import org.simantics.db.ServiceLocator;
import org.simantics.db.Database.Session.ClusterIds;
import org.simantics.db.Database.Session.ResourceSegment;
+import org.simantics.db.ServiceLocator;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.impl.ClusterBase;
import org.simantics.db.impl.ClusterI;
import org.simantics.db.impl.ClusterSupport;
import org.simantics.db.procore.cluster.ClusterTraits;
-import org.simantics.db.server.ProCoreException;
import org.simantics.db.service.ClusterSetsSupport;
import org.simantics.db.service.ClusterUID;
import org.simantics.utils.threads.logger.ITask;
this.dbFolder = dbFolder;
}
- public ArrayList<String> getChanges(long changeSetId) {
+ public ArrayList<String> getChanges(long changeSetId) throws AcornAccessVerificationException, IllegalAcornStateException {
ChangeSetInfo info = csLRU.getWithoutMutex(changeSetId);
info.acquireMutex();
try {
info.makeResident();
return info.getCSSIds();
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
info.releaseMutex();
}
}
- public ClusterBase getClusterByClusterKey(int clusterKey) throws DatabaseException {
+ public ClusterBase getClusterByClusterKey(int clusterKey) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException {
return clusterLRU.getClusterByClusterKey(clusterKey);
}
- public ClusterBase getClusterByClusterUIDOrMake(ClusterUID clusterUID) throws DatabaseException {
+ public ClusterBase getClusterByClusterUIDOrMake(ClusterUID clusterUID) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException {
return clusterLRU.getClusterByClusterUIDOrMake(clusterUID);
}
- public ClusterImpl getClusterByClusterUIDOrMakeProxy(ClusterUID clusterUID) throws DatabaseException {
+ public ClusterImpl getClusterByClusterUIDOrMakeProxy(ClusterUID clusterUID) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException {
return clusterLRU.getClusterByClusterUIDOrMakeProxy(clusterUID);
}
- public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) {
+ public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) throws AcornAccessVerificationException {
return clusterLRU.getClusterKeyByClusterUIDOrMake(clusterUID);
}
- public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) {
+ public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) throws IllegalAcornStateException, AcornAccessVerificationException {
return clusterLRU.getClusterKeyByClusterUIDOrMakeWithoutMutex(clusterUID);
}
- public int getClusterKeyByUID(long id1, long id2) throws DatabaseException {
+ public int getClusterKeyByUID(long id1, long id2) throws DatabaseException, IllegalAcornStateException {
return clusterLRU.getClusterKeyByUIDWithoutMutex(id1, id2);
}
- public <T extends ClusterI> T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException {
+ public <T extends ClusterI> T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException {
return clusterLRU.getClusterProxyByResourceKey(resourceKey);
}
- public ClusterUID getClusterUIDByResourceKey(int resourceKey) throws DatabaseException {
+ public ClusterUID getClusterUIDByResourceKey(int resourceKey) throws DatabaseException, AcornAccessVerificationException {
return clusterLRU.getClusterUIDByResourceKey(resourceKey);
}
- public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws DatabaseException {
+ public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws DatabaseException, IllegalAcornStateException, AcornAccessVerificationException {
return clusterLRU.getClusterUIDByResourceKeyWithoutMutex(resourceKey);
}
}
}
- public synchronized boolean makeSnapshot(ServiceLocator locator, boolean force) throws IOException {
-
- // Maximum autosave frequency is per 60s
- if(!force && System.nanoTime() - lastSnapshot < 10*1000000000L) {
-// System.err.println("lastSnapshot too early");
- return false;
- }
-
- // Cluster files are always there
- // Nothing has been written => no need to do anything
- long amountOfFiles = countFiles(workingDirectory);
- if(!force && amountOfFiles < 3) {
-// System.err.println("amountOfFiles < 3");
- return false;
- }
-
- System.err.println("makeSnapshot");
-
- // Schedule writing of all data to disk
- refreshHeadState();
-
- // Wait for all files to be written
- clusterLRU.shutdown();
- fileLRU.shutdown();
- streamLRU.shutdown();
- csLRU.shutdown();
-
- persistHeadState();
-
- mainState.save(dbFolder);
-
- ClusterSetsSupport cssi = locator.getService(ClusterSetsSupport.class);
- cssi.save();
-
- amountOfFiles = countFiles(workingDirectory);
-
- System.err.println(" -finished: amount of files is " + amountOfFiles);
-
- workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir));
- if (!Files.exists(workingDirectory)) {
- Files.createDirectories(workingDirectory);
- }
-
- cssi.updateReadAndWriteDirectories(lastSessionDirectory, workingDirectory);
-
- clusterLRU.setWriteDir(workingDirectory);
- fileLRU.setWriteDir(workingDirectory);
- streamLRU.setWriteDir(workingDirectory);
- csLRU.setWriteDir(workingDirectory);
-
- clusterLRU.resume();
- fileLRU.resume();
- streamLRU.resume();
- csLRU.resume();
-
- lastSnapshot = System.nanoTime();
-
- return true;
-
+ // 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 AtomicBoolean safeToMakeSnapshot = new AtomicBoolean(true);
+ private IllegalAcornStateException cause;
+
+ public synchronized boolean makeSnapshot(ServiceLocator locator, boolean fullSave) throws IllegalAcornStateException {
+ try {
+ if (!safeToMakeSnapshot.get())
+ throw cause;
+ // Maximum autosave frequency is per 60s
+ if(!fullSave && System.nanoTime() - lastSnapshot < 10*1000000000L) {
+ // System.err.println("lastSnapshot too early");
+ return false;
+ }
+
+ // Cluster files are always there
+ // Nothing has been written => no need to do anything
+ long amountOfFiles = countFiles(workingDirectory);
+ if(!fullSave && amountOfFiles < 3) {
+ // System.err.println("amountOfFiles < 3");
+ return false;
+ }
+
+ System.err.println("makeSnapshot");
+
+ // Schedule writing of all data to disk
+ refreshHeadState();
+
+ // Wait for all files to be written
+ clusterLRU.shutdown();
+ fileLRU.shutdown();
+ streamLRU.shutdown();
+ csLRU.shutdown();
+
+ // Lets check if it is still safe to make a snapshot
+ if (!safeToMakeSnapshot.get())
+ throw cause;
+
+ persistHeadState();
+
+ if (fullSave)
+ mainState.save(dbFolder);
+
+ ClusterSetsSupport cssi = locator.getService(ClusterSetsSupport.class);
+ cssi.save();
+
+ amountOfFiles = countFiles(workingDirectory);
+
+ System.err.println(" -finished: amount of files is " + amountOfFiles);
+
+ workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir));
+ if (!Files.exists(workingDirectory)) {
+ Files.createDirectories(workingDirectory);
+ }
+
+ cssi.updateWriteDirectory(workingDirectory);
+
+ clusterLRU.setWriteDir(workingDirectory);
+ fileLRU.setWriteDir(workingDirectory);
+ streamLRU.setWriteDir(workingDirectory);
+ csLRU.setWriteDir(workingDirectory);
+
+ clusterLRU.resume();
+ fileLRU.resume();
+ streamLRU.resume();
+ csLRU.resume();
+
+ lastSnapshot = System.nanoTime();
+
+ return true;
+ } catch (IllegalAcornStateException e) {
+ notSafeToMakeSnapshot(e);
+ throw e;
+ } catch (IOException e) {
+ IllegalAcornStateException e1 = new IllegalAcornStateException(e);
+ notSafeToMakeSnapshot(e1);
+ throw e1;
+ }
}
- public void refreshHeadState() throws IOException {
-
+ private void refreshHeadState() throws IOException, IllegalAcornStateException {
state.clusters.clear();
state.files.clear();
state.stream.clear();
fileLRU.persist(state.files);
streamLRU.persist(state.stream);
csLRU.persist(state.cs);
-
}
- public void persistHeadState() throws IOException {
-
+ private void persistHeadState() throws IOException {
// Sync current working directory
Files.walk(workingDirectory, 1).filter(Files::isRegularFile).forEach(FileIO::uncheckedSyncPath);
state.save(workingDirectory);
// }
- private void acquireAll() {
+ private void acquireAll() throws IllegalAcornStateException {
clusterLRU.acquireMutex();
fileLRU.acquireMutex();
streamLRU.acquireMutex();
clusterLRU.releaseMutex();
}
+ private AtomicBoolean rollback = new AtomicBoolean(false);
+
+ boolean rolledback() {
+ return rollback.get();
+ }
+
public void load() throws IOException {
// Main state
- mainState = MainState.load(dbFolder);
+ mainState = MainState.load(dbFolder, t -> rollback.set(true));
lastSessionDirectory = dbFolder.resolve(Integer.toString(mainState.headDir - 1));
throw new IOException("Could not load HeadState due to corruption", e);
}
}
-
- workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir));
- Files.createDirectories(workingDirectory);
-
- csLRU = new LRU<Long, ChangeSetInfo>("Change Set", workingDirectory);
- streamLRU = new LRU<String, ClusterStreamChunk>("Cluster Stream", workingDirectory);
- clusterLRU = new ClusterLRU(this, "Cluster", workingDirectory);
- fileLRU = new LRU<String, FileInfo>("External Value", workingDirectory);
-
- acquireAll();
-
- // Clusters
- for (String clusterKey : state.clusters) {
- String[] parts1 = clusterKey.split("#");
- String[] parts = parts1[0].split("\\.");
- long first = new BigInteger(parts[0], 16).longValue();
- long second = new BigInteger(parts[1], 16).longValue();
- ClusterUID uuid = ClusterUID.make(first, second);
- Path readDir = dbFolder.resolve(parts1[1]);
- int offset = Integer.parseInt(parts1[2]);
- int length = Integer.parseInt(parts1[3]);
- clusterLRU.map(new ClusterInfo(this, clusterLRU, readDir, uuid, offset, length));
- }
- // Files
- for (String fileKey : state.files) {
-// System.err.println("loadFile: " + fileKey);
- String[] parts = fileKey.split("#");
- Path readDir = dbFolder.resolve(parts[1]);
- int offset = Integer.parseInt(parts[2]);
- int length = Integer.parseInt(parts[3]);
- FileInfo info = new FileInfo(fileLRU, readDir, parts[0], offset, length);
- fileLRU.map(info);
- }
- // Update chunks
- for (String fileKey : state.stream) {
-// System.err.println("loadStream: " + fileKey);
- String[] parts = fileKey.split("#");
- Path readDir = dbFolder.resolve(parts[1]);
- int offset = Integer.parseInt(parts[2]);
- int length = Integer.parseInt(parts[3]);
- ClusterStreamChunk info = new ClusterStreamChunk(this,
- streamLRU, readDir, parts[0], offset, length);
- streamLRU.map(info);
- }
- // Change sets
- for (String fileKey : state.cs) {
- String[] parts = fileKey.split("#");
- Path readDir = dbFolder.resolve(parts[1]);
- Long revisionId = Long.parseLong(parts[0]);
- int offset = Integer.parseInt(parts[2]);
- int length = Integer.parseInt(parts[3]);
- ChangeSetInfo info = new ChangeSetInfo(csLRU, readDir, revisionId, offset, length);
- csLRU.map(info);
+ try {
+ workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir));
+ Files.createDirectories(workingDirectory);
+
+ csLRU = new LRU<Long, ChangeSetInfo>(this, "Change Set", workingDirectory);
+ streamLRU = new LRU<String, ClusterStreamChunk>(this, "Cluster Stream", workingDirectory);
+ clusterLRU = new ClusterLRU(this, "Cluster", workingDirectory);
+ fileLRU = new LRU<String, FileInfo>(this, "External Value", workingDirectory);
+
+ acquireAll();
+
+ // Clusters
+ for (String clusterKey : state.clusters) {
+ String[] parts1 = clusterKey.split("#");
+ String[] parts = parts1[0].split("\\.");
+ long first = new BigInteger(parts[0], 16).longValue();
+ long second = new BigInteger(parts[1], 16).longValue();
+ ClusterUID uuid = ClusterUID.make(first, second);
+ Path readDir = dbFolder.resolve(parts1[1]);
+ int offset = Integer.parseInt(parts1[2]);
+ int length = Integer.parseInt(parts1[3]);
+ clusterLRU.map(new ClusterInfo(this, clusterLRU, readDir, uuid, offset, length));
+ }
+ // Files
+ for (String fileKey : state.files) {
+ // System.err.println("loadFile: " + fileKey);
+ String[] parts = fileKey.split("#");
+ Path readDir = dbFolder.resolve(parts[1]);
+ int offset = Integer.parseInt(parts[2]);
+ int length = Integer.parseInt(parts[3]);
+ FileInfo info = new FileInfo(fileLRU, readDir, parts[0], offset, length);
+ fileLRU.map(info);
+ }
+ // Update chunks
+ for (String fileKey : state.stream) {
+ // System.err.println("loadStream: " + fileKey);
+ String[] parts = fileKey.split("#");
+ Path readDir = dbFolder.resolve(parts[1]);
+ int offset = Integer.parseInt(parts[2]);
+ int length = Integer.parseInt(parts[3]);
+ ClusterStreamChunk info = new ClusterStreamChunk(this,
+ streamLRU, readDir, parts[0], offset, length);
+ streamLRU.map(info);
+ }
+ // Change sets
+ for (String fileKey : state.cs) {
+ String[] parts = fileKey.split("#");
+ Path readDir = dbFolder.resolve(parts[1]);
+ Long revisionId = Long.parseLong(parts[0]);
+ int offset = Integer.parseInt(parts[2]);
+ int length = Integer.parseInt(parts[3]);
+ ChangeSetInfo info = new ChangeSetInfo(csLRU, readDir, revisionId, offset, length);
+ csLRU.map(info);
+ }
+
+ releaseAll();
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ // ROLLBACK ONE DIR UNTIL WE ARE FINE!
+ throw new IOException(e);
}
-
- releaseAll();
-
}
- public <T> T clone(ClusterUID uid, ClusterCreator creator)
- throws DatabaseException {
+ public <T> T clone(ClusterUID uid, ClusterCreator creator) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException, IOException {
clusterLRU.ensureUpdates(uid);
ClusterInfo info = clusterLRU.getWithoutMutex(uid);
return info.clone(uid, creator);
-
}
//private int loadCounter = 0;
static Map<String, ITask> tasks = new HashMap<String, ITask>();
- public void update(ClusterUID uid, ClusterImpl clu) {
-
+ public void update(ClusterUID uid, ClusterImpl clu) throws AcornAccessVerificationException, IllegalAcornStateException {
ClusterInfo info = clusterLRU.getWithoutMutex(uid);
info.acquireMutex();
try {
info.update(clu);
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
info.releaseMutex();
}
-
}
public long getClusterIdOrCreate(ClusterUID clusterUID) {
return 1;
}
- public int getResourceKey(ClusterUID uid, int index) {
+ public int getResourceKey(ClusterUID uid, int index) throws AcornAccessVerificationException {
return clusterLRU.getResourceKey(uid, index);
}
- public int getResourceKeyWitoutMutex(ClusterUID uid, int index) {
+ public int getResourceKeyWitoutMutex(ClusterUID uid, int index) throws IllegalAcornStateException {
return clusterLRU.getResourceKeyWithoutMutex(uid, index);
}
- public ClusterIds getClusterIds() throws ProCoreException {
-
+ public ClusterIds getClusterIds() throws IllegalAcornStateException {
clusterLRU.acquireMutex();
try {
-
Collection<ClusterInfo> infos = clusterLRU.values();
final int status = infos.size();
final long[] firsts = new long[status];
};
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
clusterLRU.releaseMutex();
}
-
}
- public void addIntoCurrentChangeSet(String ccs) {
-
+ public void addIntoCurrentChangeSet(String ccs) throws IllegalAcornStateException {
csLRU.acquireMutex();
try {
-
currentChanges.add(ccs);
-
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
-
csLRU.releaseMutex();
-
}
-
}
- public void commitChangeSet(long changeSetId, byte[] data) {
+ public void commitChangeSet(long changeSetId, byte[] data) throws IllegalAcornStateException {
csLRU.acquireMutex();
try {
ArrayList<String> csids = new ArrayList<String>(currentChanges);
currentChanges = new ArrayList<String>();
new ChangeSetInfo(csLRU, changeSetId, data, csids);
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
csLRU.releaseMutex();
}
}
- public byte[] getMetadata(long changeSetId) {
+ public byte[] getMetadata(long changeSetId) throws AcornAccessVerificationException, IllegalAcornStateException {
ChangeSetInfo info = csLRU.getWithoutMutex(changeSetId);
if (info == null) return null;
- info.acquireMutex();
- try {
- return info.getMetadataBytes();
- } catch (Throwable t) {
- throw new IllegalStateException(t);
+ info.acquireMutex();
+ try {
+ return info.getMetadataBytes();
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new IllegalAcornStateException(t);
} finally {
info.releaseMutex();
}
-
}
- public byte[] getResourceFile(final byte[] clusterUID,
- final int resourceIndex) throws ProCoreException {
+ public byte[] getResourceFile(final byte[] clusterUID, final int resourceIndex) throws AcornAccessVerificationException, IllegalAcornStateException {
ClusterUID uid = ClusterUID.make(clusterUID, 0);
String key = uid.toString() + "_" + resourceIndex;
info.acquireMutex();
try {
return info.getResourceFile();
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ throw e;
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
info.releaseMutex();
}
-
}
- public ResourceSegment getResourceSegment(final byte[] clusterUID,
- final int resourceIndex, final long segmentOffset, short segmentSize)
- throws ProCoreException {
-
+ public ResourceSegment getResourceSegment(final byte[] clusterUID, final int resourceIndex, final long segmentOffset, short segmentSize) throws AcornAccessVerificationException, IllegalAcornStateException {
ClusterUID uid = ClusterUID.make(clusterUID, 0);
String key = uid.toString() + "_" + resourceIndex;
try {
return info.getResourceSegment(clusterUID, resourceIndex, segmentOffset, segmentSize);
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
info.releaseMutex();
}
-
}
- public void modiFileEx(ClusterUID uid, int resourceKey, long offset,
- long size, byte[] bytes, long pos, ClusterSupport support) {
-
+ public void modiFileEx(ClusterUID uid, int resourceKey, long offset, long size, byte[] bytes, long pos, ClusterSupport support) throws IllegalAcornStateException {
try {
-
- String key = uid.toString()
- + "_"
- + ClusterTraits
- .getResourceIndexFromResourceKey(resourceKey);
+ String key = uid.toString() + "_" + ClusterTraits.getResourceIndexFromResourceKey(resourceKey);
FileInfo info = null;
-
fileLRU.acquireMutex();
-
try {
-
info = fileLRU.get(key);
- if (info == null)
+ if (info == null) {
info = new FileInfo(fileLRU, key, (int) (offset + size));
-
-
+ }
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
-
fileLRU.releaseMutex();
-
}
info.acquireMutex();
try {
info.updateData(bytes, offset, pos, size);
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
info.releaseMutex();
}
-
} catch (DatabaseException e) {
e.printStackTrace();
}
-
}
public void shutdown() {
csLRU.shutdown();
}
+ public void notSafeToMakeSnapshot(IllegalAcornStateException t) {
+ this.safeToMakeSnapshot.compareAndSet(true, false);
+ this.cause = t;
+ }
+
}
ByteBuffer bb = ByteBuffer.wrap(bytes, 0, length);
try (FileChannel fc = FileChannel.open(path, options, NO_ATTRIBUTES)) {
fc.write(bb);
+
+ writePosition += length;
+ if(TRACE_PERF) {
+ long duration = System.nanoTime()-start;
+ double ds = 1e-9*duration;
+ System.err.println("Wrote " + bytes.length + " bytes @ " + 1e-6*bytes.length / ds + "MB/s");
+ }
+ return result;
+ } catch (Throwable t) {
+ throw new IOException("An error occured file saving bytes for file " + path.toAbsolutePath().toString(), t);
}
-
- writePosition += length;
- if(TRACE_PERF) {
- long duration = System.nanoTime()-start;
- double ds = 1e-9*duration;
- System.err.println("Wrote " + bytes.length + " bytes @ " + 1e-6*bytes.length / ds + "MB/s");
- }
- return result;
}
public synchronized byte[] readBytes(int offset, int length) throws IOException {
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.internal.ClusterChange;
import org.simantics.acorn.internal.ClusterUpdateProcessorBase;
import org.simantics.acorn.internal.UndoClusterUpdateProcessor;
import org.simantics.db.server.ProCoreException;
import org.simantics.db.service.ClusterSetsSupport;
import org.simantics.db.service.ClusterUID;
+import org.simantics.db.service.LifecycleSupport;
import org.simantics.utils.datastructures.Pair;
import org.simantics.utils.logging.TimeLogger;
private ExecutorService executor = Executors.newSingleThreadExecutor(new ClientThreadFactory("Core Main Program", false));
private ExecutorService saver = Executors.newSingleThreadExecutor(new ClientThreadFactory("Core Snapshot Saver", true));
- private static GraphClientImpl2 INSTANCE;
private Path dbFolder;
private final Database database;
private ServiceLocator locator;
this.clusters = new ClusterManager(dbFolder);
load();
ClusterSetsSupport cssi = locator.getService(ClusterSetsSupport.class);
- cssi.updateReadAndWriteDirectories(clusters.lastSessionDirectory, clusters.workingDirectory);
+ cssi.setReadDirectory(clusters.lastSessionDirectory);
mainProgram = new MainProgram(this, clusters);
executor.execute(mainProgram);
- INSTANCE = this;
}
public Path getDbFolder() {
public void tryMakeSnapshot() throws IOException {
- if (isClosing)
+ if (isClosing || unexpectedClose)
return;
saver.execute(new Runnable() {
} finally {
mainProgram.mutex.release();
}
- } catch (IOException e) {
- Logger.defaultLogError(e);
- } catch (ProCoreException e) {
+ } catch (IllegalAcornStateException | ProCoreException e) {
Logger.defaultLogError(e);
+ unexpectedClose = true;
} catch (InterruptedException e) {
Logger.defaultLogError(e);
} finally {
try {
if(tr != null)
endTransaction(tr.getTransactionId());
+ if (unexpectedClose) {
+ LifecycleSupport support = getServiceLocator().getService(LifecycleSupport.class);
+ try {
+ support.close();
+ } catch (DatabaseException e1) {
+ Logger.defaultLogError(e1);
+ }
+ }
} catch (ProCoreException e) {
Logger.defaultLogError(e);
}
}
}
-
});
}
- public void makeSnapshot(boolean force) throws IOException {
- if (safeToMakeSnapshot)
- clusters.makeSnapshot(locator, force);
+ public void makeSnapshot(boolean fullSave) throws IllegalAcornStateException {
+ clusters.makeSnapshot(locator, fullSave);
}
public <T> T clone(ClusterUID uid, ClusterCreator creator) throws DatabaseException {
- return clusters.clone(uid, creator);
+ try {
+ return clusters.clone(uid, creator);
+ } catch (AcornAccessVerificationException | IllegalAcornStateException | IOException e) {
+ unexpectedClose = true;
+ throw new DatabaseException(e);
+ }
}
// private void save() throws IOException {
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;
+ private boolean unexpectedClose = false;
@Override
public void close() throws ProCoreException {
if(!closed && !isClosing) {
isClosing = true;
try {
- makeSnapshot(true);
+ if (!unexpectedClose)
+ makeSnapshot(true);
mainProgram.close();
clusters.shutdown();
System.err.println("executorTerminated=" + executorTerminated + ", saverTerminated=" + saverTerminated);
- INSTANCE = null;
mainProgram = null;
executor = null;
saver = null;
- } catch (IOException | InterruptedException e) {
+ } catch (IllegalAcornStateException | InterruptedException e) {
throw new ProCoreException(e);
}
+ closed = true;
}
- closed = true;
//impl.close();
}
@Override
public void acceptCommit(long transactionId, long changeSetId, byte[] metadata) throws ProCoreException {
-
clusters.state.headChangeSetId++;
-
long committedChangeSetId = changeSetId + 1;
-
- clusters.commitChangeSet(committedChangeSetId, metadata);
-
- clusters.state.transactionId = transactionId;
-
- mainProgram.committed();
-
- TimeLogger.log("Accepted commit");
-
+ try {
+ clusters.commitChangeSet(committedChangeSetId, metadata);
+
+ clusters.state.transactionId = transactionId;
+
+ mainProgram.committed();
+
+ TimeLogger.log("Accepted commit");
+ } catch (IllegalAcornStateException e) {
+ throw new ProCoreException(e);
+ }
}
@Override
- public long cancelCommit(long transactionId, long changeSetId,
- byte[] metadata, OnChangeSetUpdate onChangeSetUpdate)
- throws ProCoreException {
- safeToMakeSnapshot = false;
- throw new UnsupportedOperationException("org.simantics.acorn.GraphClientImpl2.cancelCommit() is not supported operation! Closing down to prevent further havoc");
+ public long cancelCommit(long transactionId, long changeSetId, byte[] metadata, OnChangeSetUpdate onChangeSetUpdate) throws ProCoreException {
+ UnsupportedOperationException e = new UnsupportedOperationException("org.simantics.acorn.GraphClientImpl2.cancelCommit() is not supported operation! Closing down to prevent further havoc");
+ clusters.notSafeToMakeSnapshot(new IllegalAcornStateException(e));
+ throw e;
// System.err.println("GraphClientImpl2.cancelCommit() called!! this is experimental and might cause havoc!");
// try {
// undo(new long[] {changeSetId}, onChangeSetUpdate);
* This method cannot be synchronized since it waits and must support multiple entries
* by query thread(s) and internal transactions such as snapshot saver
*/
- public Transaction askWriteTransaction()
- throws ProCoreException {
+ public Transaction askWriteTransaction() throws IllegalAcornStateException {
Semaphore semaphore = new Semaphore(0);
-
TransactionRequest req = queue(TransactionState.WRITE, semaphore);
try {
semaphore.acquire();
} catch (InterruptedException e) {
- throw new ProCoreException(e);
+ throw new IllegalAcornStateException(e);
}
-
mainProgram.startTransaction(clusters.state.headChangeSetId+1);
-
return makeTransaction(req);
-
}
public synchronized long endTransaction(long transactionId) throws ProCoreException {
}
@Override
- public Transaction askWriteTransaction(final long transactionId)
- throws ProCoreException {
- return transactionManager.askWriteTransaction();
+ public Transaction askWriteTransaction(final long transactionId) throws ProCoreException {
+ try {
+ if (isClosing || unexpectedClose || closed) {
+ throw new ProCoreException("GraphClientImpl2 is already closing so no more write transactions allowed!");
+ }
+ return transactionManager.askWriteTransaction();
+ } catch (IllegalAcornStateException e) {
+ throw new ProCoreException(e);
+ }
}
@Override
}
@Override
- public byte[] getChangeSetMetadata(long changeSetId)
- throws ProCoreException {
- return clusters.getMetadata(changeSetId);
+ public byte[] getChangeSetMetadata(long changeSetId) throws ProCoreException {
+ try {
+ return clusters.getMetadata(changeSetId);
+ } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
+ throw new ProCoreException(e);
+ }
}
@Override
@Override
public ClusterIds getClusterIds() throws ProCoreException {
- return clusters.getClusterIds();
+ try {
+ return clusters.getClusterIds();
+ } catch (IllegalAcornStateException e) {
+ throw new ProCoreException(e);
+ }
}
@Override
}
- public byte[] getResourceFile(final byte[] clusterUID, final int resourceIndex) throws ProCoreException {
+ public byte[] getResourceFile(final byte[] clusterUID, final int resourceIndex) throws ProCoreException, AcornAccessVerificationException, IllegalAcornStateException {
return clusters.getResourceFile(clusterUID, resourceIndex);
}
@Override
- public ResourceSegment getResourceSegment(final byte[] clusterUID,
- final int resourceIndex, final long segmentOffset, short segmentSize) throws ProCoreException {
-
- return clusters.getResourceSegment(clusterUID, resourceIndex, segmentOffset, segmentSize);
-
+ public ResourceSegment getResourceSegment(final byte[] clusterUID, final int resourceIndex, final long segmentOffset, short segmentSize) throws ProCoreException {
+ try {
+ return clusters.getResourceSegment(clusterUID, resourceIndex, segmentOffset, segmentSize);
+ } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
+ throw new ProCoreException(e);
+ }
}
@Override
@Override
public void updateCluster(byte[] operations) throws ProCoreException {
-
- ClusterUpdateOperation operation = new ClusterUpdateOperation(clusters, operations);
- ClusterInfo info = clusters.clusterLRU.getOrCreate(operation.uid, true);
- if(info == null) throw new IllegalStateException();
- info.acquireMutex();
- try {
+ ClusterInfo info = null;
+ try {
+ ClusterUpdateOperation operation = new ClusterUpdateOperation(clusters, operations);
+ info = clusters.clusterLRU.getOrCreate(operation.uid, true);
+ if(info == null)
+ throw new IllegalAcornStateException("info == null for operation " + operation);
+ info.acquireMutex();
info.scheduleUpdate();
mainProgram.schedule(operation);
- } catch (Throwable t) {
- throw new IllegalStateException(t);
- } finally {
- info.releaseMutex();
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ throw new ProCoreException(e);
+ } finally {
+ if (info != null)
+ info.releaseMutex();
}
-
}
- private UndoClusterUpdateProcessor getUndoCSS(String ccsId) throws DatabaseException {
+ private UndoClusterUpdateProcessor getUndoCSS(String ccsId) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException {
String[] ss = ccsId.split("\\.");
String chunkKey = ss[0];
int chunkOffset = Integer.parseInt(ss[1]);
ClusterStreamChunk chunk = clusters.streamLRU.getWithoutMutex(chunkKey);
- if(chunk == null) throw new IllegalStateException("Cluster Stream Chunk " + chunkKey + " was not found.");
+ if(chunk == null) throw new IllegalAcornStateException("Cluster Stream Chunk " + chunkKey + " was not found.");
chunk.acquireMutex();
try {
return chunk.getUndoProcessor(clusters, chunkOffset, ccsId);
+ } catch (DatabaseException e) {
+ throw e;
} catch (Throwable t) {
throw new IllegalStateException(t);
} finally {
chunk.releaseMutex();
}
-
}
- private void performUndo(String ccsId, ArrayList<Pair<ClusterUID, byte[]>> clusterChanges, UndoClusterSupport support) throws ProCoreException, DatabaseException {
-
+ private void performUndo(String ccsId, ArrayList<Pair<ClusterUID, byte[]>> clusterChanges, UndoClusterSupport support) throws ProCoreException, DatabaseException, IllegalAcornStateException, AcornAccessVerificationException {
UndoClusterUpdateProcessor proc = getUndoCSS(ccsId);
int clusterKey = clusters.getClusterKeyByClusterUIDOrMakeWithoutMutex(proc.getClusterUID());
Entry e = proc.entries.get(proc.entries.size() - 1 - i);
e.process(clusters, cs, clusterKey);
-
}
-
cs.flush();
} finally {
clusters.clusterLRU.releaseMutex();
}
-
}
@Override
public boolean undo(long[] changeSetIds, OnChangeSetUpdate onChangeSetUpdate) throws SDBException {
-
- final ArrayList<Pair<ClusterUID, byte[]>> clusterChanges = new ArrayList<Pair<ClusterUID, byte[]>>();
-
- UndoClusterSupport support = new UndoClusterSupport(clusters);
-
- final int changeSetId = clusters.state.headChangeSetId;
-
- if(ClusterUpdateProcessorBase.DEBUG)
- System.err.println(" === BEGIN UNDO ===");
-
- for(int i=0;i<changeSetIds.length;i++) {
- final long id = changeSetIds[changeSetIds.length-1-i];
- ArrayList<String> ccss = clusters.getChanges(id);
- for(int j=0;j<ccss.size();j++) {
- try {
- if(ClusterUpdateProcessorBase.DEBUG)
- System.err.println("performUndo " + ccss.get(ccss.size()-j-1));
- performUndo(ccss.get(ccss.size()-j-1), clusterChanges, support);
- } catch (DatabaseException e) {
- e.printStackTrace();
- }
- }
- }
-
- if(ClusterUpdateProcessorBase.DEBUG)
- System.err.println(" === END UNDO ===");
-
- for(int i=0;i<clusterChanges.size();i++) {
-
- final int changeSetIndex = i;
-
- final Pair<ClusterUID, byte[]> pair = clusterChanges.get(i);
-
- final ClusterUID cuid = pair.first;
- final byte[] data = pair.second;
-
- onChangeSetUpdate.onChangeSetUpdate(new ChangeSetUpdate() {
-
- @Override
- public long getChangeSetId() {
- return changeSetId;
- }
-
- @Override
- public int getChangeSetIndex() {
- return 0;
- }
-
- @Override
- public int getNumberOfClusterChangeSets() {
- return clusterChanges.size();
- }
-
- @Override
- public int getIndexOfClusterChangeSet() {
- return changeSetIndex;
- }
-
- @Override
- public byte[] getClusterId() {
- return cuid.asBytes();
- }
-
- @Override
- public boolean getNewCluster() {
- return false;
- }
-
- @Override
- public byte[] getData() {
- return data;
- }
-
- });
-
- }
-
-
+ try {
+ final ArrayList<Pair<ClusterUID, byte[]>> clusterChanges = new ArrayList<Pair<ClusterUID, byte[]>>();
+
+ UndoClusterSupport support = new UndoClusterSupport(clusters);
+
+ final int changeSetId = clusters.state.headChangeSetId;
+
+ if(ClusterUpdateProcessorBase.DEBUG)
+ System.err.println(" === BEGIN UNDO ===");
+
+ for(int i=0;i<changeSetIds.length;i++) {
+ final long id = changeSetIds[changeSetIds.length-1-i];
+ ArrayList<String> ccss = clusters.getChanges(id);
+
+ for(int j=0;j<ccss.size();j++) {
+ try {
+ if(ClusterUpdateProcessorBase.DEBUG)
+ System.err.println("performUndo " + ccss.get(ccss.size()-j-1));
+ performUndo(ccss.get(ccss.size()-j-1), clusterChanges, support);
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ if(ClusterUpdateProcessorBase.DEBUG)
+ System.err.println(" === END UNDO ===");
+
+ for(int i=0;i<clusterChanges.size();i++) {
+
+ final int changeSetIndex = i;
+
+ final Pair<ClusterUID, byte[]> pair = clusterChanges.get(i);
+
+ final ClusterUID cuid = pair.first;
+ final byte[] data = pair.second;
+
+ onChangeSetUpdate.onChangeSetUpdate(new ChangeSetUpdate() {
+
+ @Override
+ public long getChangeSetId() {
+ return changeSetId;
+ }
+
+ @Override
+ public int getChangeSetIndex() {
+ return 0;
+ }
+
+ @Override
+ public int getNumberOfClusterChangeSets() {
+ return clusterChanges.size();
+ }
+
+ @Override
+ public int getIndexOfClusterChangeSet() {
+ return changeSetIndex;
+ }
+
+ @Override
+ public byte[] getClusterId() {
+ return cuid.asBytes();
+ }
+
+ @Override
+ public boolean getNewCluster() {
+ return false;
+ }
+
+ @Override
+ public byte[] getData() {
+ return data;
+ }
+
+ });
+ }
+ } catch (AcornAccessVerificationException | IllegalAcornStateException e1) {
+ throw new ProCoreException(e1);
+ }
return false;
-
- }
-
- public static GraphClientImpl2 getInstance() {
- return INSTANCE;
}
public ServiceLocator getServiceLocator() {
return false;
}
+ @Override
+ public boolean rolledback() {
+ return clusters.rolledback();
+ }
+
package org.simantics.acorn;
import java.io.ByteArrayInputStream;
-import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
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;
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<String> ccs = new ArrayList<String>();
public static HeadState load(Path directory) throws InvalidHeadStateException {
- Path f = directory.resolve("head.state");
+ Path f = directory.resolve(HEAD_STATE);
try {
byte[] bytes = Files.readAllBytes(f);
- MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
+ MessageDigest sha1 = MessageDigest.getInstance(SHA_1);
int digestLength = sha1.getDigestLength();
sha1.update(bytes, digestLength, bytes.length - digestLength);
"Checksum " + Arrays.toString(newChecksum) + " does not match excpected "
+ Arrays.toString(Arrays.copyOfRange(bytes, 0, digestLength)) + " for " + f.toAbsolutePath());
}
-
- HeadState object = (HeadState) org.simantics.databoard.Files.readFile(new ByteArrayInputStream(bytes, digestLength, bytes.length - digestLength), Bindings.getBindingUnchecked(HeadState.class));
- return object;
-
+ 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);
}
public void save(Path directory) throws IOException {
- Path f = directory.resolve("head.state");
+ Path f = directory.resolve(HEAD_STATE);
try {
BinaryMemory rf = new BinaryMemory(4096);
try {
byte[] bytes = rf.toByteBuffer().array();
- MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
+ MessageDigest sha1 = MessageDigest.getInstance(SHA_1);
sha1.update(bytes);
byte[] checksum = sha1.digest();
public static void validateHeadStateIntegrity(Path headState) throws InvalidHeadStateException, IOException {
try {
byte[] bytes = Files.readAllBytes(headState);
- MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
+ MessageDigest sha1 = MessageDigest.getInstance(SHA_1);
int digestLength = sha1.getDigestLength();
sha1.update(bytes, digestLength, bytes.length - digestLength);
byte[] newChecksum = sha1.digest();
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.lru.ClusterStreamChunk;
import org.simantics.acorn.lru.ClusterUpdateOperation;
import org.simantics.db.service.ClusterUID;
} catch (InterruptedException e) {
e.printStackTrace();
}
-
}
-
}
// long sss = System.nanoTime();
final List<ClusterUpdateOperation> ops = updateSchedules[i];
if (!ops.isEmpty()) {
acquireAmount++;
- clusterUpdateThreads[i].execute(() -> {
-
- //long st = System.nanoTime();
- for(ClusterUpdateOperation op : ops) {
- op.run();
- }
- s.release();
- // long duration = System.nanoTime()-st;
- // elapsed.addAndGet(duration);
- // double dur = 1e-9*duration;
- // if(dur > 0.05)
- // System.err.println("duration=" + dur + "s. " + ops.size());
- });
+ clusterUpdateThreads[i].submit(new Callable<Object>() {
+
+ @Override
+ public Object call() throws Exception {
+ //long st = System.nanoTime();
+ try {
+ for(ClusterUpdateOperation op : ops) {
+ op.run();
+ }
+ } finally {
+ s.release();
+ }
+ return null;
+
+ // long duration = System.nanoTime()-st;
+ // elapsed.addAndGet(duration);
+ // double dur = 1e-9*duration;
+ // if(dur > 0.05)
+ // System.err.println("duration=" + dur + "s. " + ops.size());
+ }
+ });
}
}
clusters.streamLRU.acquireMutex();
try {
swapChunks();
- } catch (Throwable t) {
- throw new IllegalStateException(t);
+ } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
+ e.printStackTrace();
} finally {
clusters.streamLRU.releaseMutex();
}
try {
swapCS();
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
clusters.csLRU.releaseMutex();
}
} finally {
deathBarrier.release();
}
-
}
/*
* Mutex for streamLRU is assumed here
*
*/
- private void swapChunks() {
+ private void swapChunks() throws AcornAccessVerificationException, IllegalAcornStateException {
// Cache chunks during update operations
boolean written = clusters.streamLRU.swap(Integer.MAX_VALUE, CHUNK_CACHE_SIZE);
while(written) {
written = clusters.streamLRU.swap(Integer.MAX_VALUE, CHUNK_CACHE_SIZE);
}
-
}
- private void swapCS() {
+ private void swapCS() throws AcornAccessVerificationException, IllegalAcornStateException {
// Cache chunks during update operations
boolean written = clusters.csLRU.swap(Integer.MAX_VALUE, CHUNK_CACHE_SIZE);
while(written) {
written = clusters.csLRU.swap(Integer.MAX_VALUE, CHUNK_CACHE_SIZE);
}
-
}
public synchronized void committed() {
}
- public synchronized void schedule(ClusterUpdateOperation operation) {
+ public synchronized void schedule(ClusterUpdateOperation operation) throws IllegalAcornStateException {
if (!alive) {
System.err.println("Trying to schedule operation after MainProgram is closed! Operation is " + operation);
// return;
swapChunks();
notifyAll();
-
+ } catch (IllegalAcornStateException e) {
+ throw e;
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
-
clusters.streamLRU.releaseMutex();
-
}
-
}
@Override
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)) {
+ // 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());
import java.io.IOException;
import java.nio.file.Path;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
+import org.simantics.db.exception.SDBException;
+
public interface Persistable {
void toFile(Path path) throws IOException ;
- void fromFile(byte[] data);
+ void fromFile(byte[] data) throws IllegalAcornStateException, AcornAccessVerificationException;
}
\ No newline at end of file
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import org.simantics.acorn.AcornSessionManagerImpl;
import org.simantics.acorn.GraphClientImpl2;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.backup.BackupException;
import org.simantics.backup.IBackupProvider;
import org.simantics.db.server.ProCoreException;
private static final String IDENTIFIER = "AcornBackupProvider";
private long trId = -1;
private final Semaphore lock = new Semaphore(1);
+ private final GraphClientImpl2 client;
+ public AcornBackupProvider() {
+ this.client = AcornSessionManagerImpl.getInstance().getClient();
+ }
+
private static Path getAcornMetadataFile(Path dbFolder) {
return dbFolder.getParent().resolve(IDENTIFIER);
}
try {
if (trId != -1)
throw new IllegalStateException(this + " backup provider is already locked");
- trId = GraphClientImpl2.getInstance().askWriteTransaction(-1)
- .getTransactionId();
+ trId = client.askWriteTransaction(-1).getTransactionId();
} catch (ProCoreException e) {
e.printStackTrace();
}
try {
lock.acquire();
- GraphClientImpl2 client = GraphClientImpl2.getInstance();
client.makeSnapshot(true);
Path dbDir = client.getDbFolder();
throw new BackupException("Failed to lock Acorn for backup.", e);
} catch (NumberFormatException e) {
throw new BackupException("Failed to read Acorn head state file.", e);
- } catch (IOException e) {
+ } catch (IllegalAcornStateException | IOException e) {
throw new BackupException("I/O problem during Acorn backup.", e);
} finally {
if (releaseLock)
try {
if (trId == -1)
throw new BackupException(this + " backup provider is not locked");
- GraphClientImpl2.getInstance().endTransaction(trId);
+ client.endTransaction(trId);
trId = -1;
} catch (ProCoreException e) {
throw new BackupException(e);
// 1. Resolve initial backup restore target.
// This can be DB directory directly or a temporary directory that
// will replace the DB directory.
- Path dbRoot = GraphClientImpl2.getInstance().getDbFolder();
+ Path dbRoot = client.getDbFolder();
Path restorePath = dbRoot;
if (!Files.exists(dbRoot, LinkOption.NOFOLLOW_LINKS)) {
Files.createDirectories(dbRoot);
import java.io.InputStream;
import java.util.Arrays;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.internal.ClusterChange;
import org.simantics.acorn.internal.ClusterStream;
import org.simantics.acorn.internal.ClusterSupport2;
try {
return resourceTable.getValue(valueTable, resourceIndex);
} catch (ExternalValueException e) {
- return clusterSupport.impl.getResourceFile(clusterUID.asBytes(), resourceIndex);
-// return support.getValueEx(resourceIndex, clusterUID.second);
+ try {
+ return clusterSupport.impl.getResourceFile(clusterUID.asBytes(), resourceIndex);
+ } catch (AcornAccessVerificationException | IllegalAcornStateException e1) {
+ throw new DatabaseException(e1);
+ }
}
}
@Override
import java.io.InputStream;
import java.util.Arrays;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.internal.ClusterChange;
import org.simantics.acorn.internal.ClusterStream;
import org.simantics.acorn.internal.ClusterSupport2;
import org.simantics.acorn.internal.DebugPolicy;
import org.simantics.db.Resource;
+import org.simantics.db.common.utils.Logger;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.exception.ExternalValueException;
import org.simantics.db.exception.ValidationException;
-import org.simantics.db.impl.ClusterBase;
import org.simantics.db.impl.ClusterI;
import org.simantics.db.impl.ClusterSupport;
import org.simantics.db.impl.ClusterTraitsBase;
}
}
@Override
- public byte[] getValue(int resourceKey, ClusterSupport support)
- throws DatabaseException {
+ public byte[] getValue(int resourceKey, ClusterSupport support) throws DatabaseException {
if (DEBUG)
System.out.println("ClusterSmall.getValue " + resourceKey);
int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(resourceKey);
return resourceTable.getUsedSize();
}
- public int getNumberOfResources() {
-
- if(proxy) throw new IllegalStateException();
+ public int getNumberOfResources() throws IllegalAcornStateException {
+ if(proxy)
+ throw new IllegalAcornStateException("proxy == true for " + clusterId);
return resourceTable.getUsedSize();
-
}
@Override
});
return "ClusterSmall[" + getClusterUID() + " - " + getClusterId() + " - " + getNumberOfResources() + " - " + foreignTable.getResourceHashMap().size() + " - " + set.size() + "]";
} catch (DatabaseException e) {
- return "ClusterSmall[" + getNumberOfResources() + "]";
+ try {
+ return "ClusterSmall[" + getNumberOfResources() + "]";
+ } catch (IllegalAcornStateException e1) {
+ Logger.defaultLogError(e1);
+ e1.printStackTrace();
+ return "An exception occured!!";
+ }
}
}
--- /dev/null
+package org.simantics.acorn.exception;
+
+import org.simantics.db.exception.SDBException;
+
+public class AcornAccessVerificationException extends SDBException {
+
+ private static final long serialVersionUID = 6601855907356895356L;
+
+ public AcornAccessVerificationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public AcornAccessVerificationException(String message) {
+ super(message);
+ }
+
+ public AcornAccessVerificationException(Throwable cause) {
+ super(cause);
+ }
+}
--- /dev/null
+package org.simantics.acorn.exception;
+
+import org.simantics.db.exception.SDBException;
+
+public class IllegalAcornStateException extends SDBException {
+
+ private static final long serialVersionUID = -8255505454138490120L;
+
+ public IllegalAcornStateException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public IllegalAcornStateException(String message) {
+ super(message);
+ }
+
+ public IllegalAcornStateException(Throwable cause) {
+ super(cause);
+ }
+
+}
-package org.simantics.acorn;
+package org.simantics.acorn.exception;
public class InvalidHeadStateException extends Exception {
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.simantics.acorn.ClusterManager;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.db.Session;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.impl.ClusterBase;
@Override
public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) {
- return impl.getClusterKeyByClusterUIDOrMakeWithoutMutex(clusterUID);
+ try {
+ return impl.getClusterKeyByClusterUIDOrMakeWithoutMutex(clusterUID);
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
}
}
- public ResourceSegment getResourceSegment(int resourceIndex, ClusterUID clusterUID, long offset, short size)
- throws DatabaseException {
+ public ResourceSegment getResourceSegment(int resourceIndex, ClusterUID clusterUID, long offset, short size) throws DatabaseException {
if (DEBUG)
System.out.println("DEBUG: getResourceSegment ri=" + resourceIndex + " cid=" + clusterUID + " offset=" + offset + " size=" + size);
- org.simantics.db.Database.Session.ResourceSegment t = impl.getResourceSegment(clusterUID.asBytes(), resourceIndex, offset, size);
- return new ResourceSegment(t.getValueSize(), t.getSegment());
-
+ try {
+ org.simantics.db.Database.Session.ResourceSegment t = impl.getResourceSegment(clusterUID.asBytes(), resourceIndex, offset, size);
+ return new ResourceSegment(t.getValueSize(), t.getSegment());
+ } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
+ throw new DatabaseException(e);
+ }
}
protected byte[] getValueBig(ClusterBase cluster, int resourceIndex, int offset, int length) throws DatabaseException {
ClusterUID clusterUID = cluster.clusterUID;
- return impl.getResourceFile(clusterUID.asBytes(), resourceIndex);
-
+ try {
+ return impl.getResourceFile(clusterUID.asBytes(), resourceIndex);
+ } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
+ throw new DatabaseException(e);
+ }
}
protected InputStream getValueStreamBig(ClusterBase cluster, final int resourceIndex, int offset, int length) throws DatabaseException {
@Override
public int read() throws IOException {
- if(left <= 0) throw new IllegalStateException();
+ if(left <= 0)
+ throw new IOException("left <= 0 for " + _s);
if(offset == _s.bytes.length) {
short slen = (short)Math.min(left, IMAX);
import org.simantics.acorn.ClusterManager;
import org.simantics.acorn.cluster.ClusterImpl;
-import org.simantics.acorn.lru.CachingClusterSupport;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.lru.ClusterUpdateOperation;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.impl.ClusterSupport;
}
- public ClusterImpl process(ClusterImpl cluster) {
+ public ClusterImpl process(ClusterImpl cluster) throws IllegalAcornStateException {
this.cluster = cluster;
process();
info.finish();
package org.simantics.acorn.internal;
import org.simantics.acorn.cluster.ClusterImpl;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.lru.ClusterUpdateOperation;
import org.simantics.db.impl.ClusterSupport;
this.info = info;
}
- public void process(ClusterImpl cluster) {
+ public void process(ClusterImpl cluster) throws IllegalAcornStateException {
this.cluster = cluster;
process();
info.finish();
import java.util.Map;
import org.simantics.acorn.ClusterManager;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.internal.ClusterStream.ClusterEnum;
import org.simantics.acorn.internal.ClusterStream.Data;
import org.simantics.acorn.internal.ClusterStream.StmEnum;
final Map<ClusterUID, Integer> clusterKeyCache = new HashMap<ClusterUID, Integer>();
- public int getResourceKey(ClusterUID uid, int index) {
+ public int getResourceKey(ClusterUID uid, int index) throws IllegalAcornStateException {
Integer match = clusterKeyCache.get(uid);
if(match != null) return match+index;
int key = manager.getResourceKeyWitoutMutex(uid, 0);
int lows[] = new int[2];
int foreignRefs[] = new int[2];
- private void processStatement(int op, StmEnum stmEnum, ClusterEnum p, ClusterEnum o) {
+ private void processStatement(int op, StmEnum stmEnum, ClusterEnum p, ClusterEnum o) throws IllegalAcornStateException {
int curPos = pos-1-24;
}
- public void process() {
+ public void process() throws IllegalAcornStateException {
foreignPos = 0;
package org.simantics.acorn.internal;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.db.service.Bytes;
import org.simantics.db.service.ClusterUID;
pos+=4;
}
- public void process() {
+ public void process() throws IllegalAcornStateException {
while(pos < len) {
processUndoValue(op);
break;
default:
- throw new IllegalStateException();
+ throw new IllegalAcornStateException("Can not process cluster " + uid);
}
-
}
-
}
abstract void setImmutable(boolean value);
import java.util.List;
import org.simantics.acorn.ClusterManager;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.lru.ClusterChangeSet;
import org.simantics.acorn.lru.ClusterStreamChunk;
import org.simantics.acorn.lru.ClusterChangeSet.Entry;
this.ccs = ccs;
}
- private static byte[] readOperation(ClusterManager manager, ClusterStreamChunk chunk, ClusterChangeSet ccs) {
+ private static byte[] readOperation(ClusterManager manager, ClusterStreamChunk chunk, ClusterChangeSet ccs) throws AcornAccessVerificationException, IllegalAcornStateException {
// ClusterStreamChunk chunk;
// manager.streamLRU.acquireMutex();
import java.util.ArrayList;
import java.util.Arrays;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.db.service.Bytes;
import org.simantics.utils.datastructures.Pair;
private ArrayList<String> clusterChangeSetIds;
// Stub
- public ChangeSetInfo(LRU<Long, ChangeSetInfo> LRU, Path readDir, Long revision, int offset, int length) {
+ public ChangeSetInfo(LRU<Long, ChangeSetInfo> LRU, Path readDir, Long revision, int offset, int length) throws AcornAccessVerificationException {
super(LRU, revision, readDir, "clusterStream", offset, length, false, false);
LRU.map(this);
}
// New
- public ChangeSetInfo(LRU<Long, ChangeSetInfo> LRU, Long revision, byte[] bytes, ArrayList<String> clusterChangeSetIds) {
+ public ChangeSetInfo(LRU<Long, ChangeSetInfo> LRU, Long revision, byte[] bytes, ArrayList<String> clusterChangeSetIds) throws AcornAccessVerificationException {
super(LRU, revision, LRU.getDirectory(), "clusterStream", true, true);
this.metadataBytes = bytes;
this.metadataBytes = bytes;
LRU.insert(this, accessTime);
}
- public ArrayList<String> getCSSIds() {
+ public ArrayList<String> getCSSIds() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return clusterChangeSetIds;
}
- public byte[] getMetadataBytes() {
-
- if(VERIFY) verifyAccess();
+ public byte[] getMetadataBytes() throws AcornAccessVerificationException, IllegalAcornStateException {
+ if(VERIFY)
+ verifyAccess();
makeResident();
-
return metadataBytes;
-
}
private static void writeLE(TByteArrayList bytes, int value) {
import java.util.ArrayList;
import org.simantics.acorn.ClusterManager;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.internal.Change;
import org.simantics.acorn.internal.ClusterChange;
import org.simantics.db.procore.cluster.ClusterTraits;
this.newValue = null;
}
- public Entry(int subject, boolean oldValueEx, byte[] oldValue, byte[] newValue) {
+ public Entry(int subject, boolean oldValueEx, byte[] oldValue, byte[] newValue) throws IllegalAcornStateException {
if(oldValue == null && newValue == null)
- throw new IllegalStateException();
+ throw new IllegalAcornStateException("oldValue == null && newValue == null");
this.type = Type.VALUE;
this.subject = (short)(subject & 0xFFF);
this.predicate = 0;
this.newValue = newValue;
}
- public void process(ClusterManager clusters, ClusterChange cs, int clusterKey) {
+ public void process(ClusterManager clusters, ClusterChange cs, int clusterKey) throws AcornAccessVerificationException {
Entry e = this;
chunkOffset = Integer.parseInt(ss[1]);
}
- public ClusterStreamChunk getChunk(ClusterManager manager) {
+ public ClusterStreamChunk getChunk(ClusterManager manager) throws AcornAccessVerificationException {
return manager.streamLRU.get(chunkKey);
}
import org.simantics.acorn.Persistable;
import org.simantics.acorn.cluster.ClusterImpl;
import org.simantics.acorn.cluster.ClusterSmall;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.cluster.ClusterImpl.ClusterTables;
import org.simantics.acorn.internal.ClusterSupport2;
import org.simantics.compressions.CompressionCodec;
import org.simantics.compressions.Compressions;
import org.simantics.db.ClusterCreator;
import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.SDBException;
import org.simantics.db.service.Bytes;
import org.simantics.db.service.ClusterUID;
import org.simantics.utils.datastructures.Pair;
public static final String COMPRESSION = "LZ4";
// Stub
- public ClusterInfo(ClusterManager manager, LRU<ClusterUID, ClusterInfo> LRU, Path readDirectory, ClusterUID uid, int offset, int length) {
+ public ClusterInfo(ClusterManager manager, LRU<ClusterUID, ClusterInfo> LRU, Path readDirectory, ClusterUID uid, int offset, int length) throws AcornAccessVerificationException {
super(LRU, uid, readDirectory, uid.toString() + ".cluster", offset, length, false, false);
this.manager = manager;
this.cluster = null;
}
// New
- public ClusterInfo(ClusterManager manager, LRU<ClusterUID, ClusterInfo> LRU, ClusterImpl cluster) {
+ public ClusterInfo(ClusterManager manager, LRU<ClusterUID, ClusterInfo> LRU, ClusterImpl cluster) throws AcornAccessVerificationException, IllegalAcornStateException {
super(LRU, cluster.getClusterUID(), LRU.getDirectory(), cluster.getClusterUID().toString() + ".cluster", true, true);
this.manager = manager;
this.cluster = cluster;
LRU.swap(getKey());
}
- public <T> T clone(ClusterUID uid, ClusterCreator creator) throws DatabaseException {
+ public <T> T clone(ClusterUID uid, ClusterCreator creator) throws IOException, AcornAccessVerificationException, IllegalAcornStateException {
// Updates have been ensured at this point
return creator.create(uid, tables.bytes, tables.ints, tables.longs);
}
} catch (IOException e) {
- throw new DatabaseException(e);
+ throw e;
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
releaseMutex();
}
}
} catch (IOException e) {
- throw new DatabaseException(e);
+ throw e;
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
releaseMutex();
}
}
@Override
- protected Pair<byte[],Integer> toBytes() {
-
+ protected Pair<byte[],Integer> toBytes() throws IllegalAcornStateException {
try {
-
byte[] raw = null;
if(cluster instanceof ClusterSmall) {
return Pair.make(result, compressedSize+4);
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
release();
}
return "cluster";
}
- public void scheduleUpdate() {
+ public void scheduleUpdate() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
}
- public ClusterImpl getForUpdate() {
-
+ public ClusterImpl getForUpdate() throws SDBException {
try {
-
acquireMutex();
assert(updateState != null);
setDirty(true);
updateState.beginUpdate();
return cluster;
-
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ throw e;
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
-
releaseMutex();
-
}
-
}
- public void update(ClusterImpl clu) {
+ public void update(ClusterImpl clu) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
}
- public ClusterImpl getCluster() {
+ public ClusterImpl getCluster() throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
}
@Override
- public boolean canBePersisted() {
+ public boolean canBePersisted() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
}
- private ClusterUpdateState getUpdateState() {
+ private ClusterUpdateState getUpdateState() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
}
- private ClusterUpdateState getUpdateStateWithoutMutex() {
+ private ClusterUpdateState getUpdateStateWithoutMutex() throws IllegalAcornStateException {
try {
acquireMutex();
return getUpdateState();
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
releaseMutex();
}
/*
* This method blocks - no locks here
*/
- public void waitForUpdates() {
+ public void waitForUpdates() throws IllegalAcornStateException {
ClusterUpdateState state = getUpdateStateWithoutMutex();
if(state != null) {
long duration = System.nanoTime() - start;
System.err.println("Wait updates to cluster " + getKey() + " for " + (1e-6 * duration) + "ms.");
}
-
}
@Override
import org.simantics.acorn.ClusterManager;
import org.simantics.acorn.cluster.ClusterImpl;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.internal.BijectionMap;
import org.simantics.db.common.utils.Logger;
import org.simantics.db.exception.ClusterDoesNotExistException;
public class ClusterLRU extends LRU<ClusterUID, ClusterInfo> {
final private BijectionMap<ClusterUID, Integer> clusterMapping = new BijectionMap<ClusterUID, Integer>();
- final private ClusterManager manager;
public ClusterLRU(ClusterManager manager, String identifier, Path writeDir) {
-
- super(identifier, writeDir);
- this.manager = manager;
+ super(manager, identifier, writeDir);
clusterMapping.map(ClusterUID.make(0,2), clusterMapping.size() + 1);
-
}
- public ClusterInfo getOrCreate(ClusterUID uid, boolean makeIfNull) {
+ public ClusterInfo getOrCreate(ClusterUID uid, boolean makeIfNull) throws IllegalAcornStateException, AcornAccessVerificationException {
try {
if (info == null) {
- if(!makeIfNull) throw new IllegalStateException("Asked for an existing cluster " + uid + " that was not found.");
+ if(!makeIfNull) throw new IllegalAcornStateException("Asked for an existing cluster " + uid + " that was not found.");
Integer clusterKey = clusterMapping.getRight(uid);
if (clusterKey == null) {
}
return info;
-
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ throw e;
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
releaseMutex();
/*
* This method waits - we have no locks here
*/
- public void ensureUpdates(ClusterUID uid) throws DatabaseException {
+ public void ensureUpdates(ClusterUID uid) throws ClusterDoesNotExistException, AcornAccessVerificationException, IllegalAcornStateException {
ClusterInfo info = getWithoutMutex(uid);
if(info == null)
throw new ClusterDoesNotExistException("Asked a cluster which does not exist: " + uid);
info.waitForUpdates();
-
}
- public ClusterInfo get(ClusterUID uid, boolean makeIfNull, boolean ensureUpdates) throws DatabaseException {
+ public ClusterInfo get(ClusterUID uid, boolean makeIfNull, boolean ensureUpdates) throws AcornAccessVerificationException, IllegalAcornStateException {
if (ensureUpdates) {
try {
if (makeIfNull) {
Logger.defaultLogError("For debug purposes, creating cluster which does not exist", e);
} else {
- throw e;
+ throw new IllegalAcornStateException(e);
}
}
}
return getOrCreate(uid, makeIfNull);
}
- public ClusterInfo get(ClusterUID uid, boolean makeIfNull) throws DatabaseException {
+ public ClusterInfo get(ClusterUID uid, boolean makeIfNull) throws AcornAccessVerificationException, IllegalAcornStateException {
return get(uid, makeIfNull, true);
-
}
- public int getResourceKey(ClusterUID uid, int index) {
+ public int getResourceKey(ClusterUID uid, int index) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
}
- public int getResourceKeyWithoutMutex(ClusterUID uid, int index) {
+ public int getResourceKeyWithoutMutex(ClusterUID uid, int index) throws IllegalAcornStateException {
acquireMutex();
try {
return getResourceKey(uid, index);
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
releaseMutex();
}
-
}
- public int createClusterKeyByClusterUID(ClusterUID uid) {
+ public int createClusterKeyByClusterUID(ClusterUID uid) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
}
- public ClusterBase getClusterByClusterUIDOrMake(ClusterUID uid) throws DatabaseException {
+ public ClusterBase getClusterByClusterUIDOrMake(ClusterUID uid) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
}
- public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) {
+ public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
}
- public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) {
+ public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) throws IllegalAcornStateException, AcornAccessVerificationException {
acquireMutex();
try {
return getClusterKeyByClusterUIDOrMake(clusterUID);
+ } catch (AcornAccessVerificationException e) {
+ throw e;
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
releaseMutex();
}
}
- public ClusterBase getClusterByClusterKey(int clusterKey) throws DatabaseException {
+ public ClusterBase getClusterByClusterKey(int clusterKey) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
info.acquireMutex();
try {
return info.getCluster();
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ throw e;
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
info.releaseMutex();
}
-
}
- public ClusterUID getClusterUIDByResourceKey(int resourceKey)
- throws DatabaseException {
+ public ClusterUID getClusterUIDByResourceKey(int resourceKey) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
}
- public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws DatabaseException {
+ public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws IllegalAcornStateException, AcornAccessVerificationException {
acquireMutex();
try {
return getClusterUIDByResourceKey(resourceKey);
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
releaseMutex();
}
}
@SuppressWarnings("unchecked")
- public <T extends ClusterI> T getClusterByClusterUIDOrMakeProxy(ClusterUID uid) throws DatabaseException {
+ public <T extends ClusterI> T getClusterByClusterUIDOrMakeProxy(ClusterUID uid) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException {
return (T) getClusterByClusterUIDOrMake(uid);
}
@SuppressWarnings("unchecked")
- public <T extends ClusterI> T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException {
+ public <T extends ClusterI> T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
}
- public int getClusterKeyByUID(long id1, long id2) throws DatabaseException {
+ public int getClusterKeyByUID(long id1, long id2) throws DatabaseException, AcornAccessVerificationException {
if(VERIFY) verifyAccess();
}
- public int getClusterKeyByUIDWithoutMutex(long id1, long id2) throws DatabaseException {
-
+ public int getClusterKeyByUIDWithoutMutex(long id1, long id2) throws DatabaseException, IllegalAcornStateException {
acquireMutex();
try {
return getClusterKeyByClusterUIDOrMake(ClusterUID.make(id1, id2));
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
releaseMutex();
}
-
}
import org.simantics.acorn.ClusterManager;
import org.simantics.acorn.Persistable;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.internal.ClusterChange;
import org.simantics.acorn.internal.UndoClusterUpdateProcessor;
import org.simantics.compressions.CompressionCodec;
public ArrayList<ClusterUpdateOperation> operations = new ArrayList<ClusterUpdateOperation>();
// Stub
- public ClusterStreamChunk(ClusterManager manager, LRU<String, ClusterStreamChunk> LRU, Path readDir, String id, int offset, int length) {
+ public ClusterStreamChunk(ClusterManager manager, LRU<String, ClusterStreamChunk> LRU, Path readDir, String id, int offset, int length) throws AcornAccessVerificationException {
super(LRU, id, readDir, "clusterStream", offset, length, false, false);
this.manager = manager;
LRU.map(this);
}
// Creation
- public ClusterStreamChunk(ClusterManager manager, LRU<String, ClusterStreamChunk> LRU, String id) {
+ public ClusterStreamChunk(ClusterManager manager, LRU<String, ClusterStreamChunk> LRU, String id) throws AcornAccessVerificationException {
super(LRU, id, LRU.getDirectory(), "clusterStream", true, true);
this.manager = manager;
LRU.insert(this, accessTime);
makeResident(true);
ClusterUpdateOperation op = operations.get(chunkOffset);
- if(op == null) throw new IllegalStateException("Cluster Update Operation " + ccsId + " was not found.");
- if(op.ccs == null) throw new IllegalStateException("Cluster ChangeSet " + ccsId + " was not found.");
+ if(op == null) throw new IllegalAcornStateException("Cluster Update Operation " + ccsId + " was not found.");
+ if(op.ccs == null) throw new IllegalAcornStateException("Cluster ChangeSet " + ccsId + " was not found.");
UndoClusterUpdateProcessor proc = new UndoClusterUpdateProcessor(clusters, this, op.ccs);
if(proc.version != ClusterChange.VERSION)
}
- public void addOperation(ClusterUpdateOperation op) {
+ public void addOperation(ClusterUpdateOperation op) throws IllegalAcornStateException {
if(committed)
- throw new IllegalStateException();
+ throw new IllegalAcornStateException("Cannot add operation " + op + " to " + this + " if commited == true");
operations.add(op);
size += op.data.length;
// if(isCommitted()) {
}
@Override
- public boolean canBePersisted() {
+ public boolean canBePersisted() throws AcornAccessVerificationException {
if(!super.canBePersisted()) return false;
if(!isCommitted()) return false;
for(ClusterUpdateOperation op : operations) {
private static StreamDecompressor decompressor = new StreamDecompressor();
@Override
- public void fromFile(byte[] data_) {
+ public void fromFile(byte[] data_) throws IllegalAcornStateException, AcornAccessVerificationException {
try {
}
}
-
operations.add(op);
-
}
-
} catch (IOException e) {
-
- throw new IllegalStateException(e);
-
- }
-
+ throw new IllegalAcornStateException(e);
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ throw e;
+ }
}
@Override
import org.simantics.acorn.ClusterManager;
import org.simantics.acorn.cluster.ClusterImpl;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.acorn.internal.ClusterChange;
import org.simantics.acorn.internal.ClusterChange2;
import org.simantics.acorn.internal.ClusterUpdateProcessor;
public ClusterChangeSet ccs;
boolean finished = false;
- public ClusterUpdateOperation(ClusterManager manager, byte[] data) {
+ public ClusterUpdateOperation(ClusterManager manager, byte[] data) throws IllegalAcornStateException, AcornAccessVerificationException {
long cuid1 = Bytes.readLE8(data, 8);
long cuid2 = Bytes.readLE8(data, 16);
this.uid = ClusterUID.make(cuid1, cuid2);
this.data = data;
this.info = manager.clusterLRU.getOrCreate(uid, true);
-
}
public void finish() {
finished = true;
}
- public void scheduled(String ccsInfoId) {
+ public void scheduled(String ccsInfoId) throws AcornAccessVerificationException, IllegalAcornStateException {
ccs = new ClusterChangeSet(ccsInfoId, uid);
chunk = ccs.getChunk(manager);
manager.addIntoCurrentChangeSet(ccsInfoId);
}
- public void run() {
+ public void run() throws AcornAccessVerificationException, IllegalAcornStateException {
ClusterUpdateOperation op = null;
byte[] data = null;
chunk.acquireMutex();
chunk.makeResident();
op = chunk.operations.get(ccs.chunkOffset);
data = op.data;
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
chunk.releaseMutex();
}
op.runWithData(data);
}
- public void runWithData(byte[] data) {
+ public void runWithData(byte[] data) throws IllegalAcornStateException, AcornAccessVerificationException {
try {
int version = Bytes.readLE4(data, 4);
processor.process(cluster);
manager.update(uid, cluster);
} else {
- throw new IllegalStateException();
+ throw new IllegalAcornStateException("unsupported clusterChange version " + version);
}
+ } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
+ throw e;
} catch (Throwable t) {
- t.printStackTrace();
+ throw new IllegalAcornStateException(t);
}
-
}
@Override
import java.nio.file.Path;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.db.Database.Session.ResourceSegment;
-import org.simantics.db.server.ProCoreException;
import org.simantics.utils.datastructures.Pair;
import gnu.trove.list.array.TByteArrayList;
private TByteArrayList bytes;
// Stub
- public FileInfo(LRU<String, FileInfo> LRU, Path readDir, String id, int offset, int length) {
+ public FileInfo(LRU<String, FileInfo> LRU, Path readDir, String id, int offset, int length) throws AcornAccessVerificationException {
super(LRU, id, readDir, id.toString() + ".extFile", offset, length, false, false);
LRU.map(this);
}
// New
- public FileInfo(LRU<String, FileInfo> LRU, String id, int size) {
+ public FileInfo(LRU<String, FileInfo> LRU, String id, int size) throws AcornAccessVerificationException {
super(LRU, id, LRU.getDirectory(), id.toString() + ".extFile", true, true);
this.bytes = new TByteArrayList(size);
LRU.insert(this, accessTime);
}
- public byte[] getResourceFile() {
+ public byte[] getResourceFile() throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
makeResident();
return bytes.toArray();
-
}
- public ResourceSegment getResourceSegment(final byte[] clusterUID,
- final int resourceIndex, final long segmentOffset, short segmentSize)
- throws ProCoreException {
+ public ResourceSegment getResourceSegment(final byte[] clusterUID, final int resourceIndex, final long segmentOffset, short segmentSize) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
makeResident();
-
try {
-
int segSize = segmentSize;
if (segSize < 0)
segSize += 65536;
segSize = Math.min(65535, bytes.size());
final long valueSize = bytes.size();
-
final byte[] segment = bytes.toArray((int) segmentOffset, segSize);
return new ResourceSegment() {
return clusterUID;
}
};
-
} catch (Throwable t) {
-
- t.printStackTrace();
-
+ throw new IllegalAcornStateException(t);
}
-
- throw new UnsupportedOperationException();
-
}
- public void updateData(byte[] newBytes, long offset, long pos, long size) {
+ public void updateData(byte[] newBytes, long offset, long pos, long size) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
makeResident();
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
+import org.simantics.acorn.ClusterManager;
import org.simantics.acorn.GraphClientImpl2;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.db.common.utils.Logger;
/*
private Thread mutexOwner;
public Map<String, WriteRunnable> pending = new HashMap<String, WriteRunnable>();
+
+ protected final ClusterManager manager;
- public LRU(String identifier, Path writeDir) {
+ public LRU(ClusterManager manager, String identifier, Path writeDir) {
+ this.manager = manager;
this.identifier = identifier;
this.writeDir = writeDir;
resume();
* Public interface
*/
- public void acquireMutex() {
-
+ public void acquireMutex() throws IllegalAcornStateException {
try {
-
while(!mutex.tryAcquire(3, TimeUnit.SECONDS)) {
System.err.println("Mutex is taking a long time to acquire - owner is " + mutexOwner);
}
-
if(VERIFY)
mutexOwner = Thread.currentThread();
-
} catch (InterruptedException e) {
- throw new IllegalStateException(e);
+ throw new IllegalAcornStateException(e);
}
}
public Thread newThread(Runnable r) {
return new Thread(r, identifier + " File Writer");
}
-
});
if (GraphClientImpl2.DEBUG)
System.err.println("Resuming LRU writers " + writers);
* This method violates the synchronization order rule between LRU and MapVAlue
* External synchronization is used to ensure correct operation
*/
- public void persist(ArrayList<String> state) {
+ public void persist(ArrayList<String> state) throws IllegalAcornStateException {
acquireMutex();
-
try {
-
for (MapValue value : values()) {
value.acquireMutex();
// for debugging purposes
try {
// Record the value
state.add(value.getStateKey());
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
value.releaseMutex();
}
}
-
+ } catch (IllegalAcornStateException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new IllegalAcornStateException("Unable to waitPending for " + this.identifier, e);
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException("Fatal error occured for " + this.identifier, t);
} finally {
releaseMutex();
}
-
}
- public MapValue getWithoutMutex(MapKey key) {
+ public MapValue getWithoutMutex(MapKey key) throws AcornAccessVerificationException, IllegalAcornStateException {
acquireMutex();
try {
return get(key);
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
releaseMutex();
}
-
}
- public MapValue get(MapKey key) {
+ public MapValue get(MapKey key) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return map.get(key);
-
}
- public void map(MapValue info) {
+ public void map(MapValue info) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
map.put(info.getKey(), info);
-
}
- public Collection<MapValue> values() {
+ public Collection<MapValue> values() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return map.values();
-
}
- public boolean swapForced() {
+ public boolean swapForced() throws IllegalAcornStateException, AcornAccessVerificationException {
acquireMutex();
try {
return swap(0, 0, null);
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
releaseMutex();
}
}
- public boolean swap(long lifeTime, int targetSize) {
+ public boolean swap(long lifeTime, int targetSize) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
return swap(lifeTime, targetSize, null);
-
}
/*
public void setWriteDir(Path dir) {
this.writeDir = dir;
-
}
* Package access
*/
- void insert(MapValue info, long accessTime) {
+ void insert(MapValue info, long accessTime) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
map.put(info.getKey(), info);
priorityQueue.put(accessTime, info.getKey());
-
}
/*
* We have access to ClusterLRU - try to refresh value if available
*/
- boolean tryRefresh(MapValue info) {
+ boolean tryRefresh(MapValue info) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
return false;
try {
-
priorityQueue.remove(info.getLastAccessTime());
info.accessed();
map.put(info.getKey(), info);
priorityQueue.put(info.getLastAccessTime(), info.getKey());
-
return true;
-
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
-
info.releaseMutex();
-
}
-
}
/*
* We have access to MapValue and no access to clusterLRU
*/
- void refresh(MapValue info, boolean needMutex) {
+ void refresh(MapValue info, boolean needMutex) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) {
if(!needMutex) verifyAccess();
map.put(info.getKey(), info);
priorityQueue.put(info.getLastAccessTime(), info.getKey());
+ } catch (AcornAccessVerificationException e) {
+ throw e;
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
-
if(needMutex)
releaseMutex();
-
}
-
}
/*
* Private implementation
*/
- public int size() {
-
+ int size() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
-
return priorityQueue.size();
-
}
- boolean swap(MapKey excluded) {
-
+ boolean swap(MapKey excluded) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
-
return swap(swapTime, swapSize, excluded);
-
}
- boolean swap(long lifeTime, int targetSize, MapKey excluded) {
+ boolean swap(long lifeTime, int targetSize, MapKey excluded) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
if(valueToSwap != null) {
if(valueToSwap.tryAcquireMutex()) {
-
try {
-
if(valueToSwap.canBePersisted()) {
valueToSwap.persist();
return true;
}
-
} catch (Throwable t) {
- throw new IllegalStateException(t);
+ throw new IllegalAcornStateException(t);
} finally {
valueToSwap.releaseMutex();
}
}
-
}
-
return false;
-
}
-
- private MapValue getValueToSwap1(long lifeTime, int targetSize, MapKey excluded) {
+ private MapValue getValueToSwap1(long lifeTime, int targetSize, MapKey excluded) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
}
return map.get(key);
-
}
-
return null;
-
}
- private MapValue getValueToSwap(long lifeTime, int targetSize, MapKey excluded) {
+ private MapValue getValueToSwap(long lifeTime, int targetSize, MapKey excluded) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
if(value.tryAcquireMutex()) {
try {
-
// This may lock the object
- if(value.canBePersisted()) return value;
+ if(value.canBePersisted())
+ return value;
// Insert back the value
refresh(value, false);
-
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
-
value.releaseMutex();
-
}
-
}
-
}
-
return null;
-
}
- private long getSwapCandidate(long lifeTime, int targetSize) {
+ private long getSwapCandidate(long lifeTime, int targetSize) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
* Tries to persist this object. Can fail if the object cannot be persisted at this time.
*
*/
- boolean persist(Object object_) {
+ boolean persist(Object object_) throws AcornAccessVerificationException {
MapValue object = (MapValue)object_;
if(VERIFY) object.verifyAccess();
if(object.isDirty()) {
-
// It is possible that this just became unpersistable. Fail here in this case.
if(!object.canBePersisted()) {
return false;
object.release();
object.setResident(false);
return false;
-
}
-
return false;
-
}
- int makeResident(Object object_, boolean keepResident) {
+ int makeResident(Object object_, boolean keepResident) throws AcornAccessVerificationException, IllegalAcornStateException {
MapValue object = (MapValue)object_;
if(VERIFY) object.verifyAccess();
try {
-
object.setForceResident(keepResident);
if(object.isResident()) {
try {
refresh(object, false);
swap(swapTime, swapSize, object.getKey());
- } catch (Throwable t) {
- throw new IllegalStateException(t);
} finally {
releaseMutex();
}
-
return data.length;
-
} catch (IOException e) {
-
- e.printStackTrace();
-
+ throw new IllegalAcornStateException("Unable to makeResident " + identifier, e);
}
-
- return 0;
-
}
static int readCounter = 0;
ScheduledThreadPoolExecutor writers;
- void waitPending(MapValue value, boolean hasMutex) {
+ void waitPending(MapValue value, boolean hasMutex) throws IOException, AcornAccessVerificationException, IllegalAcornStateException {
- WriteRunnable r = null;
+ WriteRunnable runnable = null;
boolean inProgress = false;
synchronized(pending) {
- r = pending.get(value.getKey().toString());
- if(r != null) {
- synchronized(r) {
- if(r.committed) {
+ runnable = pending.get(value.getKey().toString());
+ if(runnable != null) {
+ synchronized(runnable) {
+ if(runnable.committed) {
// just being written - just need to wait
inProgress = true;
} else {
- r.committed = true;
+ runnable.committed = true;
// we do the writing
}
}
}
}
- if(r != null) {
+ if(runnable != null) {
if(inProgress) {
// System.err.println("reader waits for WriteRunnable to finish");
try {
- r.s.acquire();
+ if(hasMutex) {
+ runnable.borrowMutex = true;
+ }
+ runnable.s.acquire();
} catch (InterruptedException e) {
- e.printStackTrace();
+ throw new IllegalAcornStateException(e);
}
} else {
// System.err.println("reader took WriteRunnable");
- try {
- r.runReally(hasMutex);
- } catch (Throwable e) {
- e.printStackTrace();
- Logger.defaultLogError(e);
- }
+ runnable.runReally(hasMutex);
}
}
-
}
public class WriteRunnable implements Runnable {
- Path bytes;
- MapValue impl;
- boolean committed = false;
+ private Path bytes;
+ private MapValue impl;
+ private boolean committed = false;
+ private boolean borrowMutex = false;
private Semaphore s = new Semaphore(0);
WriteRunnable(Path bytes, MapValue impl) {
@Override
public void run() {
- synchronized(impl) {
-
- synchronized(this) {
-
- if(committed) return;
-
- committed = true;
-
- }
- try {
- runReally(false);
- } catch (Throwable e) {
- e.printStackTrace();
- Logger.defaultLogError(e);
- }
- }
+ try {
+ synchronized(impl) {
+
+ synchronized(this) {
+
+ if(committed)
+ return;
+
+ committed = true;
+ }
+ runReally(false);
+ }
+ } catch (Throwable t) {
+ if (t instanceof IllegalAcornStateException) {
+ manager.notSafeToMakeSnapshot((IllegalAcornStateException)t);
+ } else {
+ manager.notSafeToMakeSnapshot(new IllegalAcornStateException(t));
+ }
+ t.printStackTrace();
+ Logger.defaultLogError(t);
+ }
}
- public void runReally(boolean hasMutex) throws IOException {
-
- if(!hasMutex)
- impl.acquireMutex();
-
- try {
-
- // These have been set in method persist
- assert(!impl.isResident());
- assert(!impl.isDirty());
-
- impl.toFile(bytes);
-
- synchronized(pending) {
- pending.remove(impl.getKey().toString());
- s.release(Integer.MAX_VALUE);
- }
- } finally {
- if(!hasMutex)
- impl.releaseMutex();
- }
-
- }
-
+ public void runWithMutex() throws IOException, IllegalAcornStateException, AcornAccessVerificationException {
+
+ try {
+ // These have been set in method persist
+ assert (!impl.isResident());
+ assert (!impl.isDirty());
+
+ impl.toFile(bytes);
+ } finally {
+ synchronized (pending) {
+ pending.remove(impl.getKey().toString());
+ s.release(Integer.MAX_VALUE);
+ }
+ }
+
+ }
+
+ // Fix WriteRunnable.runReally() to use LRU.MapValue mutex instead of
+ // borrowMutex
+ public void runReally(boolean hasMutex) throws IOException, IllegalAcornStateException, AcornAccessVerificationException {
+
+ if (hasMutex) {
+
+ runWithMutex();
+
+ } else {
+
+ boolean gotMutex = impl.tryAcquireMutex();
+
+ boolean done = false;
+ while (!done) {
+
+ if (gotMutex || borrowMutex) {
+ runWithMutex();
+ done = true;
+ } else {
+ System.err.println("Retry mutex acquire");
+ gotMutex = impl.tryAcquireMutex();
+ }
+
+ }
+
+ if (gotMutex)
+ impl.releaseMutex();
+
+ }
+
+ }
}
public Path getDirectory() {
*
*/
- protected void verifyAccess() {
-// assert (mutex.availablePermits() == 0);
+ protected void verifyAccess() throws AcornAccessVerificationException {
if (mutex.availablePermits() != 0)
- throw new IllegalStateException("identifier=" + identifier + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner);
+ throw new AcornAccessVerificationException("identifier=" + identifier + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner);
}
/*
import org.simantics.acorn.FileIO;
import org.simantics.acorn.Persistable;
+import org.simantics.acorn.exception.AcornAccessVerificationException;
+import org.simantics.acorn.exception.IllegalAcornStateException;
import org.simantics.utils.datastructures.Pair;
public abstract class LRUObject<MapKey, MapValue extends LRUObject<MapKey, MapValue>> implements Persistable {
return key;
}
- public void acquireMutex() {
-
+ public void acquireMutex() throws IllegalAcornStateException {
try {
-
while(!mutex.tryAcquire(3, TimeUnit.SECONDS)) {
System.err.println("Mutex is taking a long time to acquire - owner is " + mutexOwner);
}
mutexOwner = Thread.currentThread();
} catch (InterruptedException e) {
- throw new IllegalStateException(e);
+ throw new IllegalAcornStateException(e);
}
}
@Override
public void toFile(Path bytes) throws IOException {
- if(VERIFY) verifyAccess();
- Pair<byte[],Integer> pair = toBytes();
- byte[] data = pair.first;
- int length = pair.second;
- FileIO fio = FileIO.get(bytes);
- int offset = fio.saveBytes(data, length, overwrite());
- setPosition(offset, length);
- }
-
- public int makeResident() {
+ if(VERIFY) {
+ try {
+ verifyAccess();
+ } catch (AcornAccessVerificationException e) {
+ throw new IOException("Exception occured during toFile for file " + fileName, e);
+ }
+ }
+ try {
+ Pair<byte[], Integer> pair = toBytes();
+ byte[] data = pair.first;
+ int length = pair.second;
+ FileIO fio = FileIO.get(bytes);
+ int offset = fio.saveBytes(data, length, overwrite());
+ setPosition(offset, length);
+ } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
+ throw new IOException("Exception occured during toFile for file " + fileName, e);
+ }
+ }
+
+ public int makeResident() throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
return LRU.makeResident(this, false);
}
- public int makeResident(boolean keepResident) {
+ public int makeResident(boolean keepResident) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
return LRU.makeResident(this, true);
}
abstract void release();
abstract String getExtension();
- String getStateKey() {
+ String getStateKey() throws IllegalAcornStateException, AcornAccessVerificationException {
String result = getKey().toString() + "#" + getDirectory().getFileName() + "#" + getOffset() + "#" + getLength();
if(offset == -1)
- throw new IllegalStateException(result);
+ throw new IllegalAcornStateException(result);
return result;
}
- long getLastAccessTime() {
+ long getLastAccessTime() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return accessTime;
}
- void accessed() {
+ void accessed() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
accessTime = AccessTime.getInstance().getAccessTime();
}
- boolean persist() {
+ boolean persist() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
if(LRU.persist(this)) {
readDirectory = LRU.getDirectory();
}
}
- void setForceResident(boolean value) {
+ void setForceResident(boolean value) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
forceResident = value;
// isForceResidentSetAfterLastGet = true;
}
- boolean canBePersisted() {
+ boolean canBePersisted() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
// isForceResidentSetAfterLastGet = false;
return !forceResident;
}
- boolean isDirty() {
+ boolean isDirty() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return dirty;
}
- boolean isResident() {
+ boolean isResident() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return resident;
}
- String getFileName() {
+ String getFileName() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return fileName;
}
- void setResident(boolean value) {
+ void setResident(boolean value) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
resident = value;
}
- void setDirty(boolean value) {
+ void setDirty(boolean value) throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
dirty = value;
}
- byte[] readFile() throws IOException {
+ byte[] readFile() throws IOException, AcornAccessVerificationException {
if(VERIFY) verifyAccess();
Path dir = getDirectory();
Path f = dir.resolve(getFileName());
abstract protected boolean overwrite();
- abstract protected Pair<byte[],Integer> toBytes();
+ abstract protected Pair<byte[],Integer> toBytes() throws IllegalAcornStateException;
- protected void setDirty() {
+ protected void setDirty() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
dirty = true;
}
- protected void verifyAccess() {
- assert(mutex.availablePermits() == 0);
+ protected void verifyAccess() throws AcornAccessVerificationException {
+ if (mutex.availablePermits() != 0)
+ throw new AcornAccessVerificationException("fileName=" + fileName + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner);
}
- protected synchronized void cancelForceResident() {
+ protected synchronized void cancelForceResident() throws AcornAccessVerificationException {
setForceResident(false);
}
* Private implementation details
*/
- private int getOffset() {
+ private int getOffset() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return offset;
}
- private int getLength() {
+ private int getLength() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return length;
}
- private void setPosition(int offset, int length) {
+ private void setPosition(int offset, int length) throws AcornAccessVerificationException, IllegalAcornStateException {
if(VERIFY) verifyAccess();
if(offset == -1)
- throw new IllegalStateException();
+ throw new IllegalAcornStateException("offset == -1 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
this.offset = offset;
this.length = length;
if(overwrite() && offset > 0)
- throw new IllegalStateException();
+ throw new IllegalAcornStateException("overwrite() == true && offset > 0 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
}
- private Path getDirectory() {
+ private Path getDirectory() throws AcornAccessVerificationException {
if(VERIFY) verifyAccess();
return readDirectory;
}
import java.util.ArrayList;
import java.util.Arrays;
-import org.simantics.acorn.InvalidHeadStateException;
+import org.simantics.acorn.exception.InvalidHeadStateException;
public class HeadState implements Serializable {