package org.simantics.db.procore.ui.internal; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.simantics.db.common.utils.Logger; import org.simantics.db.server.Auxiliary; import org.simantics.db.server.ProCoreException; abstract class Handler { protected final String title = Messages.Handler_DatabaseServer; abstract boolean start(Shell shell, ProCoreException e) throws ProCoreException; protected void checkFolderGiven(Shell shell, ProCoreException e) throws ProCoreException { if (null == e.getDbFolder()) { String msg = Messages.Handler_NoDatabaseFolderGiven; MessageDialog.openWarning(shell, title, msg); throw new ProCoreException(msg); } } } final class DefaultHandler extends Handler { @Override boolean start(Shell shell, ProCoreException e) throws ProCoreException { String warning = Util.getMessage(e); MessageDialog.openError(shell, title, warning); return false; } } final class GuardFileVersionHandler extends Handler { @Override boolean start(Shell shell, ProCoreException e) throws ProCoreException { checkFolderGiven(shell, e); return HandlerUtil.recoverFromGuardFileVersion(shell, e.getDbFolder(), title, e.getMessage()); } } final class DatabaseCorruptedHandler extends Handler { @Override boolean start(Shell shell, ProCoreException e) throws ProCoreException { checkFolderGiven(shell, e); return HandlerUtil.recoverFromJournal(shell, e.getDbFolder(), title, e.getMessage()); } } final class DatabaseLastExitHandler extends Handler { @Override boolean start(Shell shell, ProCoreException e) throws ProCoreException { checkFolderGiven(shell, e); return HandlerUtil.recoverFromDatabaseLastExit(shell, e.getDbFolder(), title, e.getMessage()); } } final class DatabaseProtocolHandler extends Handler { @Override boolean start(Shell shell, ProCoreException e) throws ProCoreException { checkFolderGiven(shell, e); return HandlerUtil.recoverFromProtocol(shell, e.getDbFolder(), title, e.getMessage()); } } final class DatabaseStartHandler extends Handler { @Override boolean start(Shell shell, ProCoreException e) throws ProCoreException { checkFolderGiven(shell, e); String warning = e.getMessage(); MessageDialog.openError(shell, title, warning); return false; } } final class DatabaseVersionHandler extends Handler { @Override boolean start(Shell shell, ProCoreException e) throws ProCoreException { checkFolderGiven(shell, e); return HandlerUtil.recoverFromDatabaseVersion(shell, e.getDbFolder(), title, e.getMessage()); } } class HandlerUtil { private static String NL = System.getProperty("line.separator"); //$NON-NLS-1$ private static boolean isFolder(final Shell shell, final File dbFolder, String title) { if (dbFolder.isDirectory()) return true; MessageDialog.openWarning(shell, title, Messages.Handler_WarningMsgDatabaseFolderNotExists + dbFolder); return false; } public static boolean saveWithQuestion(final Shell shell, final File dbFolder, String title, String msg) { if (!isFolder(shell, dbFolder, title)) return false; // Save not possible. String question = NLS.bind(Messages.Handler_SaveDatabase,new Object[] {((null != msg) ? (msg + NL) : ""), //$NON-NLS-1$ NL , dbFolder}); boolean yes = MessageDialog.openQuestion(shell, title, question); if (!yes) return true; final class SaveDatabase extends ExecutorDatabase { Path saveFolder; SaveDatabase(File dbFolder) { super(dbFolder); beginMessage = Messages.Handler_SavingDatabaseBeginMsg; okMessage = Messages.Handler_SavingDatabaseOkMsg; failMessage = Messages.Handler_SavingDatabaseFailMsg; cancelMessage = Messages.Handler_SavingDatabaseCancelledMsg; } @Override public void execute() throws Throwable { saveFolder = Auxiliary.saveDatabase(dbFolder); if (null == saveFolder || !Files.isDirectory(saveFolder)) throw new ProCoreException("Save folder not ok."); //$NON-NLS-1$ } @Override public String getMessage() { return NLS.bind( Messages.Handler_FolderEquals ,new Object[] { NL , saveFolder}); } } SaveDatabase save = new SaveDatabase(dbFolder); execute(shell, save); save.showDone(shell); return save.ok; } static boolean saveWithCheck(final Shell shell, final File dbFolder, String title, String msg) { boolean ok = saveWithQuestion(shell, dbFolder, title, msg); if (ok) return true; String question = Messages.Handler_SaveContinue; return Util.confirm(shell, title, question); } public static boolean delete(final Shell shell, final File dbFolder, String title, String msg) { if (!isFolder(shell, dbFolder, title)) return false; // Delete not possible. String question = NLS.bind(Messages.Handler_DeleteDatabase, new Object[] {((null != msg) ? (msg + NL) : ""), NL , dbFolder}); //$NON-NLS-1$ boolean yes = MessageDialog.openQuestion(shell, title, question); if (!yes) return false; saveWithQuestion(shell, dbFolder, title, null); final class DeleteDatabase extends ExecutorDatabase { DeleteDatabase(File dbFolder) { super(dbFolder); beginMessage = Messages.Handler_DeletingDatabase; okMessage = Messages.Handler_DatabaseHasBeenDeleted; failMessage = Messages.Handler_FailedDeleteDatabase; cancelMessage = Messages.Handler_DeleteCancelled; } @Override public void execute() throws Throwable { Auxiliary.deleteDatabase(dbFolder); } } DeleteDatabase delete = new DeleteDatabase(dbFolder); execute(shell, delete); delete.showDone(shell); return delete.ok; } public static boolean purge(final Shell shell, final File dbFolder, String title, String msg) { if (!isFolder(shell, dbFolder, title)) return false; // Purge not possible. try { if (Auxiliary.purgeDatabaseDone(dbFolder)) { MessageDialog.openInformation(shell, title, NLS.bind(Messages.Handler_DatabaseAlreadyPurged, new Object[] { NL, dbFolder})); return true; // Already clean. } } catch (ProCoreException e) { Logger.defaultLogError("Failed to query database purge state.", e); //$NON-NLS-1$ } String question = ((null != msg) ? (msg + NL) : "") //$NON-NLS-1$ + Messages.Handler_PurgeDatabaseQuestion; boolean yes = MessageDialog.openQuestion(shell, title, question); if (!yes) return false; return purgeWithSave(shell, dbFolder, title); } private static boolean purgeWithSave(final Shell shell, final File dbFolder, String title) { boolean ok = saveWithCheck(shell, dbFolder, title, null); if (!ok) return false; final class PurgeDatabase extends ExecutorDatabase { PurgeDatabase(File dbFolder) { super(dbFolder); beginMessage = Messages.Handler_PurgingDatabase; okMessage = Messages.Handler_DatabaseHasBeenPurged; failMessage = Messages.Handler_FailedToPurgeDatabase; cancelMessage = Messages.Handler_PurgeCancelled; } @Override public void execute() throws Throwable { Auxiliary.purgeDatabase(dbFolder); } } PurgeDatabase purge = new PurgeDatabase(dbFolder); execute(shell, purge); purge.showDone(shell); return purge.ok; } public static boolean recoverFromGuardFileVersion(final Shell shell, final File dbFolder, String title, String msg) throws ProCoreException { String question = NLS.bind(Messages.Handler_GuardFileMisMatchQuestion, new Object[] { ((null != msg) ? msg : ""), //$NON-NLS-1$ NL}); MessageDialog.openWarning(shell, title, question); return false; } public static boolean recoverFromDatabaseLastExit(final Shell shell, final File dbFolder, String title, String msg) throws ProCoreException { String message = NLS.bind(Messages.Handler_MessageWhatToDo, new Object[] {((null != msg) ? msg : "") , NL }) ; //$NON-NLS-1$ ArrayList choices = new ArrayList(); choices.add(new Util.Choice(Messages.Handler_Cancel, Messages.Handler_CancelDescription)); choices.add(new Util.Choice(Messages.Handler_Ignore, Messages.Handler_IgnoreDescription)); choices.add(new Util.Choice(Messages.Handler_Remove, Messages.Handler_RemoveDescription)); choices.add(new Util.Choice(Messages.Handler_Recover, Messages.Handler_RecoverDescription)); Util.Choice[] t = new Util.Choice[choices.size()]; int choice = Util.select(shell, title, message, choices.toArray(t), 0); switch (choice) { default: return false; case 1: return ignoreExitStatusWithSave(shell, dbFolder, title); case 2: return purgeWithSave(shell, dbFolder, title); case 3: return recoverFromJournalWithSave(shell, dbFolder, title); } } public static boolean ignoreExitStatusWithSave(final Shell shell, final File dbFolder, String title) { boolean ok = saveWithCheck(shell, dbFolder, title, null); if (!ok) return false; final class IgnoreExitDatabase extends ExecutorDatabase { IgnoreExitDatabase(File dbFolder) { super(dbFolder); beginMessage = Messages.Handler_IgnoreExitDatabaseBeginMsg; okMessage = Messages.Handler_IgnoreExitDatabaseOkMsg; failMessage = Messages.Handler_IgnoreExitDatabaseBeginFailMsg; cancelMessage = Messages.Handler_IgnoreExitDatabaseCancelMsg; } @Override public void execute() throws Throwable { Auxiliary.ignoreExit(dbFolder); } } IgnoreExitDatabase recover = new IgnoreExitDatabase(dbFolder); execute(shell, recover); recover.showDone(shell); return recover.ok; } public static boolean ignoreProtocolVersionWithSave(final Shell shell, final File dbFolder, String title) { boolean ok = saveWithCheck(shell, dbFolder, title, null); if (!ok) return false; final class IgnoreProtocolDatabase extends ExecutorDatabase { IgnoreProtocolDatabase(File dbFolder) { super(dbFolder); beginMessage = Messages.Handler_IgnoreProtocolDatabaseBeginMsg; okMessage = Messages.Handler_IgnoreProtocolDatabaseOkMsg; failMessage = Messages.Handler_IgnoreProtocolDatabaseFailMsg; cancelMessage = Messages.Handler_IgnoreProtocolDatabaseCancelMsg; } @Override public void execute() throws Throwable { Auxiliary.ignoreProtocol(dbFolder); } } IgnoreProtocolDatabase ignore = new IgnoreProtocolDatabase(dbFolder); execute(shell, ignore); ignore.showDone(shell); return ignore.ok; } public static boolean recoverFromProtocol(final Shell shell, final File dbFolder, String title, String msg) throws ProCoreException { String question = ((null != msg) ? msg : "") + NL + Messages.Handler_ProCoreExceptionQuestion; //$NON-NLS-1$ boolean yes = Util.openDefaultNo(shell, title, question, MessageDialog.QUESTION); if (!yes) return false; return ignoreProtocolVersionWithSave(shell, dbFolder, title); } // public static boolean recoverFromProtocolWithSave(final Shell shell, final File dbFolder, String title) { // boolean ok = saveWithCheck(shell, dbFolder, title, null); // if (!ok) // return false; // return ignoreProtocolVersionWithSave(shell, dbFolder, title); // } public static boolean recoverFromDatabaseVersion(final Shell shell, final File dbFolder, String title, String msg) throws ProCoreException { String question = ((null != msg) ? msg : "") + NL + Messages.Handler_ProCoreException2; //$NON-NLS-1$ boolean yes = Util.openDefaultNo(shell, title, question, MessageDialog.QUESTION); if (!yes) return false; return recoverFromJournalWithSave(shell, dbFolder, title); } public static boolean recoverFromJournal(final Shell shell, final File dbFolder, String title, String msg) throws ProCoreException { if (!isFolder(shell, dbFolder, title)) return false; // Recovery not possible. if (!Auxiliary.canReadJournal(dbFolder)) { MessageDialog.openWarning(shell, title, NLS.bind(Messages.Handler_JournalFileNotExists ,new Object[] { NL , dbFolder})); return false; // Recovery not possible. } String question = ((null != msg) ? msg : "") //$NON-NLS-1$ + NL + Messages.Handler_RecreateDatabaseFromJournalQuestion; boolean yes = MessageDialog.openQuestion(shell, title, question); if (!yes) return false; return recoverFromJournalWithSave(shell, dbFolder, title); } public static boolean recoverFromJournalWithSave(final Shell shell, final File dbFolder, String title) { boolean ok = saveWithCheck(shell, dbFolder, title, null); if (!ok) return false; final class RecoverDatabase extends ExecutorDatabase { RecoverDatabase(File dbFolder) { super(dbFolder); beginMessage = Messages.Handler_RecoveringDatabaseBeginMsg; okMessage = Messages.Handler_RecoveringDatabaseRecoverdMsg; failMessage = Messages.Handler_RecoveringDatabaseFailedMsg; cancelMessage = Messages.Handler_RecoveringDatabaseCancelledMsg; } @Override public void execute() throws Throwable { Auxiliary.replaceFromJournal(dbFolder); } } RecoverDatabase recover = new RecoverDatabase(dbFolder); execute(shell, recover); recover.showDone(shell); return recover.ok; } private static void sleep(long millsec) throws InterruptedException { Display display = UI.getDisplay(); boolean isUIThread = (null == display) ? false : (Thread.currentThread() == display.getThread()); Thread.sleep(100); if (!isUIThread) return; int count = 0; while (++count < 1000 && display.readAndDispatch()) continue; } interface Executor extends Runnable { public String getMessageBegin(); public String getMessageCancel(); public String getMessageFail(Throwable throwable); public String getMessageFail(); public String getMessageOk(); public boolean isDone(); public boolean isForkable(); public boolean isCancelable(); public void execute() throws Throwable; public void setCancelled(); public void setDone(); public void showDone(Shell shell); } static abstract class ExecutorBase implements Executor { protected String beginMessage = Messages.Handler_ExecutorBaseBeginMsg; protected String okMessage = Messages.Handler_ExecutorBaseOkMsg; protected String failMessage = Messages.Handler_ExecutorBaseFailedMsg; protected String cancelMessage = Messages.Handler_ExecutorBaseCancelledMsg; protected boolean done = false; protected boolean ok = false; protected boolean cancelled = false; protected boolean forkable = true; protected boolean cancelable = false; protected Throwable throwable = null; public void run() { try { execute(); ok = true; } catch (Throwable t) { throwable = t; } finally { done = true; } } @Override public String getMessageBegin() { return beginMessage; } @Override public String getMessageCancel() { return cancelMessage; } @Override public String getMessageFail(Throwable throwable) { return failMessage + NL + throwable.getMessage(); } @Override public String getMessageFail() { return failMessage; } @Override public String getMessageOk() { return okMessage; } @Override public boolean isDone() { return done; } @Override public void setCancelled() { cancelled = true; } @Override public void setDone() { done = true; } @Override public boolean isForkable() { return forkable; } @Override public boolean isCancelable() { return cancelable; } @Override public void showDone(Shell shell) { if (null != throwable) Util.showError(shell, getMessageFail(throwable)); else if (ok) Util.showInfo(shell, getMessageOk()); else if (cancelled) Util.showInfo(shell, getMessageCancel()); else Util.showWarning(shell, getMessageFail()); } } static abstract class ExecutorDatabase extends ExecutorBase { protected final File dbFolder; ExecutorDatabase(File dbFolder) { this.dbFolder = dbFolder; } String getMessage() { return NLS.bind( Messages.Handler_FolderEquals ,new Object[] { NL , dbFolder}); } @Override public String getMessageBegin() { return super.getMessageBegin() + getMessage(); } @Override public String getMessageCancel() { return super.getMessageCancel() + getMessage(); } @Override public String getMessageFail(Throwable t) { return super.getMessageFail(t) + getMessage(); } @Override public String getMessageOk() { return super.getMessageOk() + getMessage(); } } private static void execute(final Shell shell, final Executor executor) { final Thread thread = new Thread(executor); thread.start(); IRunnableWithProgress progress = new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { monitor.beginTask(executor.getMessageBegin(), IProgressMonitor.UNKNOWN); while (!monitor.isCanceled() && !executor.isDone()) { monitor.worked(1); sleep(100); } if (executor.isDone()) return; executor.setCancelled(); thread.interrupt(); monitor.subTask(Messages.Handler_MonitorWaitingForCancellationToFinish); while (!executor.isDone()) sleep(100); } finally { monitor.done(); } } }; boolean fork = executor.isForkable(); boolean cancelable = executor.isCancelable(); try { new ProgressMonitorDialog(shell).run(fork, cancelable, progress); } catch (InvocationTargetException e) { } catch (InterruptedException e) { } } }