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