]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.procore.ui/src/org/simantics/db/procore/ui/internal/Handler.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.db.procore.ui / src / org / simantics / db / procore / ui / internal / Handler.java
1 package org.simantics.db.procore.ui.internal;
2
3 import java.io.File;
4 import java.lang.reflect.InvocationTargetException;
5 import java.nio.file.Files;
6 import java.nio.file.Path;
7 import java.util.ArrayList;
8
9 import org.eclipse.core.runtime.IProgressMonitor;
10 import org.eclipse.jface.dialogs.MessageDialog;
11 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
12 import org.eclipse.jface.operation.IRunnableWithProgress;
13 import org.eclipse.swt.widgets.Display;
14 import org.eclipse.swt.widgets.Shell;
15 import org.simantics.db.common.utils.Logger;
16 import org.simantics.db.server.Auxiliary;
17 import org.simantics.db.server.ProCoreException;
18
19 abstract class Handler {
20     protected final String title = "Database Server";
21     abstract boolean start(Shell shell, ProCoreException e) throws ProCoreException;
22     protected void checkFolderGiven(Shell shell, ProCoreException e) throws ProCoreException {
23         if (null == e.getDbFolder()) {
24             String msg = "No database folder given.";
25             MessageDialog.openWarning(shell, title, msg);
26             throw new ProCoreException(msg);
27         }
28     }
29 }
30 final class DefaultHandler extends Handler {
31     @Override
32     boolean start(Shell shell, ProCoreException e) throws ProCoreException {
33         String warning = Util.getMessage(e);
34         MessageDialog.openError(shell, title, warning);
35         return false;
36     }
37 }
38 final class GuardFileVersionHandler extends Handler {
39     @Override
40     boolean start(Shell shell, ProCoreException e) throws ProCoreException {
41         checkFolderGiven(shell, e);
42         return HandlerUtil.recoverFromGuardFileVersion(shell, e.getDbFolder(), title, e.getMessage());
43     }
44 }
45 final class DatabaseCorruptedHandler extends Handler {
46     @Override
47     boolean start(Shell shell, ProCoreException e) throws ProCoreException {
48         checkFolderGiven(shell, e);
49         return HandlerUtil.recoverFromJournal(shell, e.getDbFolder(), title, e.getMessage());
50     }
51 }
52 final class DatabaseLastExitHandler extends Handler {
53     @Override
54     boolean start(Shell shell, ProCoreException e) throws ProCoreException {
55         checkFolderGiven(shell, e);
56         return HandlerUtil.recoverFromDatabaseLastExit(shell, e.getDbFolder(), title, e.getMessage());
57
58     }
59 }
60 final class DatabaseProtocolHandler extends Handler {
61     @Override
62     boolean start(Shell shell, ProCoreException e) throws ProCoreException {
63         checkFolderGiven(shell, e);
64         return HandlerUtil.recoverFromProtocol(shell, e.getDbFolder(), title, e.getMessage());
65
66     }
67 }
68 final class DatabaseStartHandler extends Handler {
69     @Override
70     boolean start(Shell shell, ProCoreException e) throws ProCoreException {
71         checkFolderGiven(shell, e);
72         String warning =  e.getMessage();
73         MessageDialog.openError(shell, title, warning);
74         return false;
75     }
76 }
77 final class DatabaseVersionHandler extends Handler {
78     @Override
79     boolean start(Shell shell, ProCoreException e) throws ProCoreException {
80         checkFolderGiven(shell, e);
81         return HandlerUtil.recoverFromDatabaseVersion(shell, e.getDbFolder(), title, e.getMessage());
82     }
83 }
84 class HandlerUtil {
85     private static String NL = System.getProperty("line.separator");
86     private static boolean isFolder(final Shell shell, final File dbFolder, String title) {
87         if (dbFolder.isDirectory())
88             return true;
89         MessageDialog.openWarning(shell, title, "Database folder does not exist. folder=" + dbFolder);
90         return false;
91     }
92     public static boolean saveWithQuestion(final Shell shell, final File dbFolder, String title, String msg) {
93         if (!isFolder(shell, dbFolder, title))
94             return false; // Save not possible.
95         String question = ((null != msg) ? (msg + NL) : "")
96         + "Do you want to save database?" + NL + "folder=" + dbFolder;
97         boolean yes = MessageDialog.openQuestion(shell, title, question);
98         if (!yes)
99             return true;
100         final class SaveDatabase extends ExecutorDatabase {
101             Path saveFolder;
102             SaveDatabase(File dbFolder) {
103                 super(dbFolder);
104                 beginMessage = "Saving database.";
105                 okMessage = "Database has been saved.";
106                 failMessage = "Failed to save database.";
107                 cancelMessage = "Save cancelled.";
108             }
109             @Override
110             public void execute() throws Throwable {
111                 saveFolder = Auxiliary.saveDatabase(dbFolder);
112                 if (null == saveFolder || !Files.isDirectory(saveFolder))
113                     throw new ProCoreException("Save folder not ok.");
114             }
115             @Override
116             public String getMessage() {
117                 return NL + "folder=" + saveFolder;
118             }
119         }
120         SaveDatabase save = new SaveDatabase(dbFolder);
121         execute(shell, save);
122         save.showDone(shell);
123         return save.ok;
124     }
125     static boolean saveWithCheck(final Shell shell, final File dbFolder, String title, String msg) {
126         boolean ok = saveWithQuestion(shell, dbFolder, title, msg);
127         if (ok)
128             return true;
129         String question = "Save failed. Do you want me to contine?";
130         return Util.confirm(shell, title, question);
131     }
132
133     public static boolean delete(final Shell shell, final File dbFolder, String title, String msg) {
134         if (!isFolder(shell, dbFolder, title))
135             return false; // Delete not possible.
136         String question = ((null != msg) ? (msg + NL) : "")
137         + "Do you want to delete database?" + NL + "folder=" + dbFolder;
138         boolean yes = MessageDialog.openQuestion(shell, title, question);
139         if (!yes)
140             return false;
141         saveWithQuestion(shell, dbFolder, title,  null);
142         final class DeleteDatabase extends ExecutorDatabase {
143             DeleteDatabase(File dbFolder) {
144                 super(dbFolder);
145                 beginMessage = "Deleting database.";
146                 okMessage = "Database has been deleted.";
147                 failMessage = "Failed to delete database.";
148                 cancelMessage = "Delete cancelled.";
149             }
150             @Override
151             public void execute() throws Throwable {
152                 Auxiliary.deleteDatabase(dbFolder);
153             }
154         }
155         DeleteDatabase delete = new DeleteDatabase(dbFolder);
156         execute(shell, delete);
157         delete.showDone(shell);
158         return delete.ok;
159     }
160     public static boolean purge(final Shell shell, final File dbFolder, String title, String msg) {
161         if (!isFolder(shell, dbFolder, title))
162             return false; // Purge not possible.
163         try {
164             if (Auxiliary.purgeDatabaseDone(dbFolder)) {
165                 MessageDialog.openInformation(shell, title, "Database already purged." + NL + "folder=" + dbFolder);
166                 return true; // Already clean.
167             }
168         } catch (ProCoreException e) {
169             Logger.defaultLogError("Failed to query database purge state.", e);
170         }
171         String question = ((null != msg) ? (msg + NL) : "")
172         + "Do you want to purge database?";
173         boolean yes = MessageDialog.openQuestion(shell, title, question);
174         if (!yes)
175             return false;
176         return purgeWithSave(shell, dbFolder, title);
177     }
178     private static boolean purgeWithSave(final Shell shell, final File dbFolder, String title) {
179         boolean ok = saveWithCheck(shell, dbFolder, title, null);
180         if (!ok)
181             return false;
182         final class PurgeDatabase extends ExecutorDatabase {
183             PurgeDatabase(File dbFolder) {
184                 super(dbFolder);
185                 beginMessage = "Purging database.";
186                 okMessage = "Database has been purged.";
187                 failMessage = "Failed to purge database.";
188                 cancelMessage = "Purge cancelled.";
189             }
190             @Override
191             public void execute() throws Throwable {
192                 Auxiliary.purgeDatabase(dbFolder);
193             }
194         }
195         PurgeDatabase purge = new PurgeDatabase(dbFolder);
196         execute(shell, purge);
197         purge.showDone(shell);
198         return purge.ok;
199     }
200     public static boolean recoverFromGuardFileVersion(final Shell shell, final File dbFolder, String title, String msg)
201     throws ProCoreException {
202         String question = ((null != msg) ? msg : "")
203         + NL + "Guard file version mismatch indicates that the database was made with different server version."
204         + "It would be best to open the database with the same version it was made.";
205         MessageDialog.openWarning(shell, title, question);
206         return false;
207     }
208     public static boolean recoverFromDatabaseLastExit(final Shell shell, final File dbFolder, String title, String msg)
209     throws ProCoreException {
210         String message = ((null != msg) ? msg : "") + NL + "What should I try to do?";
211         ArrayList<Util.Choice> choices = new ArrayList<Util.Choice>();
212         choices.add(new Util.Choice("Cancel", "Cancel i.e. do nothing. Choose this if you want to manually analyze and correct the situation. This is the safest choice."));
213         choices.add(new Util.Choice("Ignore", "Ignore the exit status. Choose this if you do not know what you are doing. This is fast way to recover and is the safest choice except for cancel."));
214         choices.add(new Util.Choice("Remove", "Remove history. Choose this you know what you are doing. This is fast way to recover but can leave tricky semantic errors in the database. Furhermore, depending on the reason for the non clean exit status, this can fail and corrupt data. However, depending on how the client and/or server died, this could be the right choice."));
215         choices.add(new Util.Choice("Recover", "Recover using journal. Choose this if you are willing to wait and know that the other choices won't work."));
216         Util.Choice[] t = new Util.Choice[choices.size()];
217         int choice = Util.select(shell, title, message, choices.toArray(t), 0);
218         switch (choice) {
219             default: return false;
220             case 1: return ignoreExitStatusWithSave(shell, dbFolder, title);
221             case 2: return purgeWithSave(shell, dbFolder, title);
222             case 3: return recoverFromJournalWithSave(shell, dbFolder, title);
223         }
224     }
225     public static boolean ignoreExitStatusWithSave(final Shell shell, final File dbFolder, String title) {
226         boolean ok = saveWithCheck(shell, dbFolder, title, null);
227         if (!ok)
228             return false;
229         final class IgnoreExitDatabase extends ExecutorDatabase {
230             IgnoreExitDatabase(File dbFolder) {
231                 super(dbFolder);
232                 beginMessage = "Ignoring last exit status.";
233                 okMessage = "Ignore done.";
234                 failMessage = "Failed to start.";
235                 cancelMessage = "Ignore cancelled.";
236             }
237             @Override
238             public void execute() throws Throwable {
239                 Auxiliary.ignoreExit(dbFolder);
240             }
241         }
242         IgnoreExitDatabase recover = new IgnoreExitDatabase(dbFolder);
243         execute(shell, recover);
244         recover.showDone(shell);
245         return recover.ok;
246     }
247     public static boolean ignoreProtocolVersionWithSave(final Shell shell, final File dbFolder, String title) {
248         boolean ok = saveWithCheck(shell, dbFolder, title, null);
249         if (!ok)
250             return false;
251         final class IgnoreProtocolDatabase extends ExecutorDatabase {
252             IgnoreProtocolDatabase(File dbFolder) {
253                 super(dbFolder);
254                 beginMessage = "Ignoring protocol version mismatch.";
255                 okMessage = "Ignore done.";
256                 failMessage = "Failed to start.";
257                 cancelMessage = "Ignore cancelled.";
258             }
259             @Override
260             public void execute() throws Throwable {
261                 Auxiliary.ignoreProtocol(dbFolder);
262             }
263         }
264         IgnoreProtocolDatabase ignore = new IgnoreProtocolDatabase(dbFolder);
265         execute(shell, ignore);
266         ignore.showDone(shell);
267         return ignore.ok;
268     }
269     public static boolean recoverFromProtocol(final Shell shell, final File dbFolder, String title, String msg)
270     throws ProCoreException {
271         String question = ((null != msg) ? msg : "")
272         + NL + "Protocol version mismatch indicates that server and client versions differ."
273         + " It would be best to open the database using the same server and client version."
274         + " But if you insist I can ignore the mismatch and try to muddle along."
275         + " If this works then you should export the data and get matching client and server versions."
276         + " Otherwise there could later be strange errors caused by this version mismatch."
277         + " Shoud I try?";
278         boolean yes = Util.openDefaultNo(shell, title, question, MessageDialog.QUESTION);
279         if (!yes)
280             return false;
281         return ignoreProtocolVersionWithSave(shell, dbFolder, title);
282     }
283 //    public static boolean recoverFromProtocolWithSave(final Shell shell, final File dbFolder, String title) {
284 //        boolean ok = saveWithCheck(shell, dbFolder, title, null);
285 //        if (!ok)
286 //            return false;
287 //        return ignoreProtocolVersionWithSave(shell, dbFolder, title);
288 //    }
289     public static boolean recoverFromDatabaseVersion(final Shell shell, final File dbFolder, String title, String msg)
290     throws ProCoreException {
291         String question = ((null != msg) ? msg : "")
292         + NL + "Database version mismatch indicates that the database was made with different server version."
293         + " It would be best to open the database with the same version it was made."
294         + " But if you insist I can try to recover database from journal.";
295         boolean yes = Util.openDefaultNo(shell, title, question, MessageDialog.QUESTION);
296         if (!yes)
297             return false;
298         return recoverFromJournalWithSave(shell, dbFolder, title);
299     }
300     public static boolean recoverFromJournal(final Shell shell, final File dbFolder, String title, String msg)
301     throws ProCoreException {
302         if (!isFolder(shell, dbFolder, title))
303             return false; // Recovery not possible.
304         if (!Auxiliary.canReadJournal(dbFolder)) {
305             MessageDialog.openWarning(shell, title, "Journal file does not exist or isn't readable." + NL + "folder=" + dbFolder);
306             return false; // Recovery not possible.
307         }
308         String question = ((null != msg) ? msg : "")
309         + NL + "Do you want me to try to recreate the database from journal?";
310         boolean yes = MessageDialog.openQuestion(shell, title, question);
311         if (!yes)
312             return false;
313         return recoverFromJournalWithSave(shell, dbFolder, title);
314     }
315     public static boolean recoverFromJournalWithSave(final Shell shell, final File dbFolder, String title) {
316         boolean ok = saveWithCheck(shell, dbFolder, title, null);
317         if (!ok)
318             return false;
319         final class RecoverDatabase extends ExecutorDatabase {
320             RecoverDatabase(File dbFolder) {
321                 super(dbFolder);
322                 beginMessage = "Recovering database.";
323                 okMessage = "Database has been recovered.";
324                 failMessage = "Failed to recover database.";
325                 cancelMessage = "Recovery cancelled.";
326             }
327             @Override
328             public void execute() throws Throwable {
329                 Auxiliary.replaceFromJournal(dbFolder);
330             }
331         }
332         RecoverDatabase recover = new RecoverDatabase(dbFolder);
333         execute(shell, recover);
334         recover.showDone(shell);
335         return recover.ok;
336     }
337     private static void sleep(long millsec) throws InterruptedException {
338         Display display = UI.getDisplay();
339         boolean isUIThread = (null == display) ? false : (Thread.currentThread() == display.getThread());
340         Thread.sleep(100);
341         if (!isUIThread)
342             return;
343         int count = 0;
344         while (++count < 1000 && display.readAndDispatch())
345             continue;
346     }
347     interface Executor extends Runnable {
348         public String getMessageBegin();
349         public String getMessageCancel();
350         public String getMessageFail(Throwable throwable);
351         public String getMessageFail();
352         public String getMessageOk();
353         public boolean isDone();
354         public boolean isForkable();
355         public boolean isCancelable();
356         public void execute() throws Throwable;
357         public void setCancelled();
358         public void setDone();
359         public void showDone(Shell shell);
360     }
361     static abstract class ExecutorBase implements Executor {
362         protected String beginMessage = "Task begin.";
363         protected String okMessage = "Task ok.";
364         protected String failMessage = "Task failed.";
365         protected String cancelMessage = "Task cancelled.";
366         protected boolean done = false;
367         protected boolean ok = false;
368         protected boolean cancelled = false;
369         protected boolean forkable = true;
370         protected boolean cancelable = false;
371         protected Throwable throwable = null;
372         public void run() {
373             try {
374                 execute();
375                 ok = true;
376             } catch (Throwable t) {
377                 throwable = t;
378             } finally {
379                 done = true;
380            }
381         }
382         @Override
383         public String getMessageBegin() {
384             return beginMessage;
385         }
386         @Override
387         public String getMessageCancel() {
388             return cancelMessage;
389         }
390         @Override
391         public String getMessageFail(Throwable throwable) {
392             return failMessage + NL + throwable.getMessage();
393         }
394         @Override
395         public String getMessageFail() {
396             return failMessage;
397         }
398         @Override
399         public String getMessageOk() {
400             return okMessage;
401         }
402         @Override
403         public boolean isDone() {
404             return done;
405         }
406         @Override
407         public void setCancelled() {
408             cancelled = true;
409         }
410         @Override
411         public void setDone() {
412             done = true;
413         }
414         @Override
415         public boolean isForkable() {
416             return forkable;
417         }
418         @Override
419         public boolean isCancelable() {
420             return cancelable;
421         }
422         @Override
423         public void showDone(Shell shell) {
424             if (null != throwable)
425                 Util.showError(shell, getMessageFail(throwable));
426             else if (ok)
427                 Util.showInfo(shell, getMessageOk());
428             else if (cancelled)
429                 Util.showInfo(shell, getMessageCancel());
430             else
431                 Util.showWarning(shell, getMessageFail());
432         }
433     }
434     static abstract class ExecutorDatabase extends ExecutorBase {
435         protected final File dbFolder;
436         ExecutorDatabase(File dbFolder) {
437             this.dbFolder = dbFolder;
438         }
439         String getMessage() {
440             return NL + "folder=" + dbFolder;
441         }
442         @Override
443         public String getMessageBegin() {
444             return super.getMessageBegin() + getMessage();
445         }
446         @Override
447         public String getMessageCancel() {
448             return super.getMessageCancel() + getMessage();
449         }
450         @Override
451         public String getMessageFail(Throwable t) {
452             return super.getMessageFail(t) + getMessage();
453         }
454         @Override
455         public String getMessageOk() {
456             return super.getMessageOk() + getMessage();
457         }
458     }
459     private static void execute(final Shell shell, final Executor executor) {
460         final Thread thread = new Thread(executor);
461         thread.start();
462         IRunnableWithProgress progress = new IRunnableWithProgress() {
463             @Override
464             public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
465                 try {
466                     monitor.beginTask(executor.getMessageBegin(), IProgressMonitor.UNKNOWN);
467                     while (!monitor.isCanceled() && !executor.isDone()) {
468                         monitor.worked(1);
469                         sleep(100);
470                     }
471                     if (executor.isDone())
472                         return;
473                     executor.setCancelled();
474                     thread.interrupt();
475                     monitor.subTask("Waiting for cancellation to finish.");
476                     while (!executor.isDone())
477                         sleep(100);
478                 } finally {
479                     monitor.done();
480                 }
481             }
482         };
483         boolean fork = executor.isForkable();
484         boolean cancelable = executor.isCancelable();
485         try {
486             new ProgressMonitorDialog(shell).run(fork, cancelable, progress);
487         } catch (InvocationTargetException e) {
488         } catch (InterruptedException e) {
489         }
490     }
491 }