]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java
a423fbdbe23b815871688fb17cb2c6fb165550f7
[simantics/platform.git] / bundles / org.simantics.acorn / src / org / simantics / acorn / internal / AcornDatabase.java
1 package org.simantics.acorn.internal;
2
3 import java.io.File;
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
17 import org.simantics.acorn.GraphClientImpl2;
18 import org.simantics.db.Database;
19 import org.simantics.db.DatabaseUserAgent;
20 import org.simantics.db.ServiceLocator;
21 import org.simantics.db.server.DatabaseStartException;
22 import org.simantics.db.server.ProCoreException;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * @author Tuukka Lehtonen
28  */
29 public class AcornDatabase implements Database {
30
31     private static final Logger LOGGER = LoggerFactory.getLogger(AcornDatabase.class);
32
33     private static final String LOCK_FILE_NAME = "lock";
34
35     private final Path folder;
36     private final Path lockFile;
37
38     private GraphClientImpl2 currentClient;
39
40     private DatabaseUserAgent userAgent;
41
42     private RandomAccessFile raLockFile;
43
44     private FileLock lock;
45
46     private boolean isRunning;
47
48     public AcornDatabase(Path folder) {
49         this.folder = folder;
50         this.lockFile = folder.resolve(LOCK_FILE_NAME);
51     }
52
53     @Override
54     public DatabaseUserAgent getUserAgent() {
55         return userAgent;
56     }
57
58     @Override
59     public void setUserAgent(DatabaseUserAgent dbUserAgent) {
60         userAgent = dbUserAgent;
61     }
62
63     @Override
64     public Status getStatus() {
65         return Status.Local;
66     }
67
68     @Override
69     public File getFolder() {
70         return folder.toFile();
71     }
72
73     @Override
74     public boolean isFolderOk() {
75         return isFolderOk(folder.toFile());
76     }
77
78     @Override
79     public boolean isFolderOk(File aFolder) {
80         if (!aFolder.isDirectory())
81             return false;
82         return true;
83     }
84
85     @Override
86     public boolean isFolderEmpty() {
87         return isFolderEmpty(folder.toFile());
88     }
89
90     @Override
91     public boolean isFolderEmpty(File aFolder) {
92         Path path = aFolder.toPath();
93         if (!Files.isDirectory(path))
94             return false;
95         try (DirectoryStream<Path> folderStream = Files.newDirectoryStream(path)) {
96             return !folderStream.iterator().hasNext();
97         } catch (IOException e) {
98             LOGGER.error("Failed to open folder stream. folder=" + path, e);
99             return false;
100         }
101     }
102
103     @Override
104     public void initFolder(Properties properties) throws ProCoreException {
105         try {
106             Files.createDirectories(folder);
107         } catch (IOException e) {
108             throw new ProCoreException(e);
109         }
110     }
111
112     @Override
113     public void deleteFiles() throws ProCoreException {
114         deleteTree(folder);
115     }
116
117     @Override
118     public synchronized void start() throws ProCoreException {
119         try {
120             raLockFile = new RandomAccessFile(lockFile.toFile(), "rw");
121             lock = raLockFile.getChannel().tryLock();
122             if (lock == null) {
123                 safeLoggingClose(raLockFile, lockFile);
124                 throw new ProCoreException("The database in folder " + folder.toAbsolutePath() + " is already in use!");
125             }
126             isRunning = true;
127         } catch (IOException e) {
128             LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e);
129             safeLoggingClose(raLockFile, lockFile);
130             throw new ProCoreException("Failed to start database at " + folder.toAbsolutePath(), e);
131         }
132     }
133
134     @Override
135     public boolean isRunning() throws ProCoreException {
136         return isRunning;
137     }
138
139     @Override
140     public synchronized boolean tryToStop() throws ProCoreException {
141         if (!isRunning)
142             return false;
143         try {
144             safeLoggingClose(lock, lockFile);
145             lock = null;
146             safeLoggingClose(raLockFile, lockFile);
147             raLockFile = null;
148             Files.deleteIfExists(lockFile);
149             isRunning = false;
150         } catch (IOException e) {
151             LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e);
152         }
153         return true;
154     }
155
156     @Override
157     public void connect() throws ProCoreException {
158     }
159
160     @Override
161     public boolean isConnected() throws ProCoreException {
162         return isRunning;
163     }
164
165     @Override
166     public String execute(String command) throws ProCoreException {
167         throw new UnsupportedOperationException("execute(" + command + ")");
168     }
169
170     @Override
171     public void disconnect() throws ProCoreException {
172     }
173
174     @Override
175     public void clone(File to, int revision, boolean saveHistory) throws ProCoreException {
176         // TODO: implement
177         throw new UnsupportedOperationException();
178     }
179
180     @Override
181     public Path createFromChangeSets(int revision) throws ProCoreException {
182         // TODO: implement
183         throw new UnsupportedOperationException();
184     }
185
186     @Override
187     public void deleteGuard() throws ProCoreException {
188         // TODO: implement
189         throw new UnsupportedOperationException();
190     }
191
192     @Override
193     public Path dumpChangeSets() throws ProCoreException {
194         // TODO: implement
195         throw new UnsupportedOperationException();
196     }
197
198     @Override
199     public void purgeDatabase() throws ProCoreException {
200         if(currentClient == null) throw new IllegalStateException("No current session.");
201         currentClient.purgeDatabase();
202     }
203
204     @Override
205     public long serverGetTailChangeSetId() throws ProCoreException {
206         if(currentClient == null) throw new IllegalStateException("No current session.");
207         return currentClient.getTailChangeSetId();
208     }
209
210     @Override
211     public Session newSession(ServiceLocator locator) throws ProCoreException {
212         try {
213                 if(currentClient != null) throw new DatabaseStartException(folder.toFile(), "A session is already running. Only one session is supported.");
214                 currentClient = new GraphClientImpl2(this, folder, locator); 
215             return currentClient;
216         } catch (IOException e) {
217             throw new ProCoreException(e);
218         }
219     }
220
221     @Override
222     public Journal getJournal() throws ProCoreException {
223         // TODO: implement
224         throw new UnsupportedOperationException();
225     }
226
227     private static void deleteTree(Path path) throws ProCoreException {
228         if (!Files.exists(path))
229             return;
230
231         class Visitor extends SimpleFileVisitor<Path> {
232             @Override
233             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
234                 try {
235                     Files.delete(file);
236                 } catch (IOException ioe) {
237                     ioe.printStackTrace();
238                     throw ioe;
239                 }
240                 return FileVisitResult.CONTINUE;
241             }
242             @Override
243             public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
244                 if (e == null) {
245                     try {
246                         Files.delete(dir);
247                     } catch (IOException ioe) {
248                         ioe.printStackTrace();
249                         throw ioe;
250                     }
251                     return FileVisitResult.CONTINUE;
252                 }
253                 throw e;
254             }
255         }
256         try {
257             Visitor v = new Visitor();
258             EnumSet<FileVisitOption> opts = EnumSet.noneOf(FileVisitOption.class);
259             Files.walkFileTree(path, opts, Integer.MAX_VALUE, v);
260         } catch (IOException e) {
261             throw new ProCoreException("Could not delete " + path, e);
262         }
263     }
264
265         @Override
266         public String getCompression() {
267                 return "LZ4";
268         }
269
270     private static void safeLoggingClose(AutoCloseable closeable, Path file) {
271         if (closeable == null)
272             return;
273         try (AutoCloseable c = closeable) {
274         } catch (Exception e) {
275             LOGGER.error("Failed to close " + closeable.getClass() + " of " + file.toAbsolutePath());
276         }
277     }
278
279 }