1 package org.simantics.acorn.internal;
4 import java.io.IOException;
5 import java.io.RandomAccessFile;
6 import java.nio.channels.FileLock;
7 import java.nio.file.DirectoryStream;
8 import java.nio.file.FileVisitOption;
9 import java.nio.file.FileVisitResult;
10 import java.nio.file.Files;
11 import java.nio.file.Path;
12 import java.nio.file.SimpleFileVisitor;
13 import java.nio.file.attribute.BasicFileAttributes;
14 import java.util.EnumSet;
15 import java.util.Properties;
16 import java.util.stream.Stream;
18 import org.simantics.acorn.GraphClientImpl2;
19 import org.simantics.db.Database;
20 import org.simantics.db.DatabaseUserAgent;
21 import org.simantics.db.ServiceLocator;
22 import org.simantics.db.server.DatabaseStartException;
23 import org.simantics.db.server.ProCoreException;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
27 import fi.vtt.simantics.procore.internal.StaticSessionProperties;
30 * @author Tuukka Lehtonen
32 public class AcornDatabase implements Database {
34 private static final Logger LOGGER = LoggerFactory.getLogger(AcornDatabase.class);
36 private static final String LOCK_FILE_NAME = "lock";
38 private final Path folder;
39 private final Path lockFile;
41 private GraphClientImpl2 currentClient;
43 private DatabaseUserAgent userAgent;
45 private RandomAccessFile raLockFile;
47 private FileLock lock;
49 private boolean isRunning;
51 public AcornDatabase(Path folder) {
53 this.lockFile = folder.resolve(LOCK_FILE_NAME);
57 public DatabaseUserAgent getUserAgent() {
62 public void setUserAgent(DatabaseUserAgent dbUserAgent) {
63 userAgent = dbUserAgent;
67 public Status getStatus() {
72 public File getFolder() {
73 return folder.toFile();
77 public boolean isFolderOk() {
78 return isFolderOk(folder.toFile());
82 public boolean isFolderOk(File aFolder) {
83 if (!aFolder.isDirectory())
89 public boolean isFolderEmpty() {
90 return isFolderEmpty(folder.toFile());
94 public boolean isFolderEmpty(File aFolder) {
95 Path path = aFolder.toPath();
96 if (!Files.isDirectory(path))
98 try (DirectoryStream<Path> folderStream = Files.newDirectoryStream(path)) {
99 return !folderStream.iterator().hasNext();
100 } catch (IOException e) {
101 LOGGER.error("Failed to open folder stream. folder=" + path, e);
107 public void initFolder(Properties properties) throws ProCoreException {
109 Files.createDirectories(folder);
110 } catch (IOException e) {
111 throw new ProCoreException(e);
116 public void deleteFiles() throws ProCoreException {
118 File vgPath = StaticSessionProperties.virtualGraphStoragePath;
119 if (vgPath != null) {
120 try (Stream<Path> vgs = Files.list(vgPath.toPath())) {
121 for (Path p : vgs.toArray(Path[]::new))
123 } catch (IOException e) {
124 throw new ProCoreException(e);
130 public synchronized void start() throws ProCoreException {
132 raLockFile = new RandomAccessFile(lockFile.toFile(), "rw");
133 lock = raLockFile.getChannel().tryLock();
135 safeLoggingClose(raLockFile, lockFile);
136 throw new ProCoreException("The database in folder " + folder.toAbsolutePath() + " is already in use!");
139 } catch (IOException e) {
140 LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e);
141 safeLoggingClose(raLockFile, lockFile);
142 throw new ProCoreException("Failed to start database at " + folder.toAbsolutePath(), e);
147 public boolean isRunning() throws ProCoreException {
152 public synchronized boolean tryToStop() throws ProCoreException {
156 safeLoggingClose(lock, lockFile);
158 safeLoggingClose(raLockFile, lockFile);
160 Files.deleteIfExists(lockFile);
162 safeLoggingClose(currentClient, currentClient.getDbFolder());
163 currentClient = null;
164 } catch (IOException e) {
165 LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e);
171 public void connect() throws ProCoreException {
175 public boolean isConnected() throws ProCoreException {
180 public String execute(String command) throws ProCoreException {
181 throw new UnsupportedOperationException("execute(" + command + ")");
185 public void disconnect() throws ProCoreException {
189 public void clone(File to, int revision, boolean saveHistory) throws ProCoreException {
191 throw new UnsupportedOperationException();
195 public Path createFromChangeSets(int revision) throws ProCoreException {
197 throw new UnsupportedOperationException();
201 public void deleteGuard() throws ProCoreException {
203 throw new UnsupportedOperationException();
207 public Path dumpChangeSets() throws ProCoreException {
209 throw new UnsupportedOperationException();
213 public void purgeDatabase() throws ProCoreException {
214 if(currentClient == null) throw new IllegalStateException("No current session.");
215 currentClient.purgeDatabase();
219 public long serverGetTailChangeSetId() throws ProCoreException {
220 if(currentClient == null) throw new IllegalStateException("No current session.");
221 return currentClient.getTailChangeSetId();
225 public Session newSession(ServiceLocator locator) throws ProCoreException {
227 if(currentClient != null) throw new DatabaseStartException(folder.toFile(), "A session is already running. Only one session is supported.");
228 currentClient = new GraphClientImpl2(this, folder, locator);
229 return currentClient;
230 } catch (IOException e) {
231 throw new ProCoreException(e);
236 public Journal getJournal() throws ProCoreException {
238 throw new UnsupportedOperationException();
241 static class Visitor extends SimpleFileVisitor<Path> {
243 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
246 } catch (IOException ioe) {
247 LOGGER.error("Failed to delete file {}", file, ioe);
250 return FileVisitResult.CONTINUE;
253 public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
257 } catch (IOException ioe) {
258 LOGGER.error("Failed to delete directory {}", dir, ioe);
261 return FileVisitResult.CONTINUE;
267 private static void deleteTree(Path path) throws ProCoreException {
268 if (!Files.exists(path))
271 Files.walkFileTree(path, EnumSet.noneOf(FileVisitOption.class), Integer.MAX_VALUE, new Visitor());
272 } catch (IOException e) {
273 throw new ProCoreException("Could not delete " + path, e);
278 public String getCompression() {
282 private static void safeLoggingClose(AutoCloseable closeable, Path file) {
283 if (closeable == null)
285 try (AutoCloseable c = closeable) {
286 } catch (Exception e) {
287 LOGGER.error("Failed to close " + closeable.getClass() + " of " + file.toAbsolutePath(), e);
291 private static void safeLoggingClose(Database.Session session, Path file) {
296 } catch (Exception e) {
297 LOGGER.error("Failed to close " + session.getClass() + " of " + file.toAbsolutePath(), e);