create new branch webapp
[taxeditor.git] / eu.etaxonomy.taxeditor.webapp / src / main / java / eu / etaxonomy / taxeditor / webapp / server / CdmStoreConnectorLocal.java
diff --git a/eu.etaxonomy.taxeditor.webapp/src/main/java/eu/etaxonomy/taxeditor/webapp/server/CdmStoreConnectorLocal.java b/eu.etaxonomy.taxeditor.webapp/src/main/java/eu/etaxonomy/taxeditor/webapp/server/CdmStoreConnectorLocal.java
new file mode 100755 (executable)
index 0000000..6b2ac49
--- /dev/null
@@ -0,0 +1,305 @@
+/**
+ * Copyright (C) 2007 EDIT
+ * European Distributed Institute of Taxonomy
+ * http://www.e-taxonomy.eu
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * See LICENSE.TXT at the top of this package for the full license terms.
+ */
+
+package eu.etaxonomy.taxeditor.webapp.server;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.CancellationException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.widgets.Display;
+import org.springframework.core.io.Resource;
+
+import eu.etaxonomy.cdm.api.application.CdmApplicationController;
+import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteController;
+import eu.etaxonomy.cdm.api.application.ICdmRepository;
+import eu.etaxonomy.cdm.config.CdmSourceException;
+import eu.etaxonomy.cdm.config.ICdmSource;
+import eu.etaxonomy.cdm.database.DbSchemaValidation;
+import eu.etaxonomy.cdm.database.ICdmDataSource;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
+import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
+import eu.etaxonomy.cdm.model.name.Rank;
+import eu.etaxonomy.cdm.model.term.init.TermLoader;
+import eu.etaxonomy.taxeditor.event.EventUtility;
+import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
+import eu.etaxonomy.taxeditor.l10n.Messages;
+import eu.etaxonomy.taxeditor.model.AbstractUtility;
+import eu.etaxonomy.taxeditor.model.CdmProgressMonitorAdapter;
+import eu.etaxonomy.taxeditor.model.MessagingUtils;
+import eu.etaxonomy.taxeditor.remoting.source.ICdmRemoteSource;
+import eu.etaxonomy.taxeditor.store.CdmAuthenticationException;
+import eu.etaxonomy.taxeditor.store.CdmStore;
+import eu.etaxonomy.taxeditor.store.LoginManager;
+import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
+
+
+/**
+ * @author n.hoffmann
+ * @created Dec 8, 2010
+ */
+public class CdmStoreConnectorLocal extends Job {
+    private final Display display;
+    private final ICdmSource cdmSource;
+    private DbSchemaValidation dbSchemaValidation;
+    private final Resource applicationContextBean;
+
+    public CdmStoreConnectorLocal(Display display,
+            ICdmSource cdmSource,
+            DbSchemaValidation dbSchemaValidation,
+            Resource applicationContextBean) {
+        super(String.format(Messages.CdmStoreConnector_CREATING_DATAMODEL, cdmSource));
+        this.display = display;
+        this.cdmSource = cdmSource;
+        this.dbSchemaValidation = dbSchemaValidation;
+        this.applicationContextBean = applicationContextBean;
+    }
+
+    @Override
+    public IStatus run(final IProgressMonitor monitor) {
+
+        monitor.beginTask(getConnectionMessage(), 10);
+
+        // check if database is up and running
+        checkDatabaseReachable(monitor);
+
+        if (!monitor.isCanceled()) {
+            // check if the datasource actually holds data
+            checkIsNonEmptyCdmDatabase(monitor);
+        }
+
+        if (dbSchemaValidation != DbSchemaValidation.CREATE
+                && !monitor.isCanceled()) {
+            // if we do not create the datasource, we want to check if the
+            // datasource is compatible with this editor
+            checkDbSchemaVersionCompatibility(monitor);
+        }
+
+        // we are done with our low level checking and will free resources now
+        cdmSource.closeOpenConnections();
+
+        if (!monitor.isCanceled()) {
+            CdmStore.close(monitor);
+        }
+
+        ICdmRepository applicationController = null;
+
+        if (!monitor.isCanceled()) {
+            CdmProgressMonitorAdapter subprogressMonitor = CdmProgressMonitorAdapter
+                    .CreateSubMonitor(monitor, 7);
+            // This is where we instantiate the application controller
+            int oldPriority = Thread.currentThread().getPriority();
+            try {
+                Thread.currentThread().setPriority(10);
+                applicationController = getApplicationController(cdmSource,subprogressMonitor);
+                MessagingUtils.informationDialog(Messages.CdmStoreConnector_SUCCESS, Messages.CdmStoreConnector_DATA_MODEL_CREATION_SUCCESSFUL);
+                //FIXME E4 migrate or delete because data source view is not used anymore
+//                CdmDataSourceViewPartE4 dataSourceView = (CdmDataSourceViewPartE4) WorkbenchUtility.getE4WrappedPart(AbstractUtility.getView("eu.etaxonomy.taxeditor.view.datasource", false));
+//                if(dataSourceView!=null){
+//                    dataSourceView.refresh();
+//                }
+                EventUtility.postEvent(WorkbenchEventConstants.REFRESH_DATASOURCE, true);
+                return Status.OK_STATUS;
+            } catch (Exception e) {
+                if(! causeIsCancelationExceptionRecursive(e)){
+                    MessagingUtils.errorDialog(Messages.CdmStoreConnector_COULD_NOT_CREATE_DATAMODEL, CdmStoreConnectorLocal.class,
+                               String.format(Messages.CdmStoreConnector_ERROR_DURING_DATAMODEL_CREATION, cdmSource.getName()), TaxeditorStorePlugin.PLUGIN_ID, e, true);
+                    return Status.CANCEL_STATUS;
+                }
+            } finally {
+                monitor.done();
+                Thread.currentThread().setPriority(oldPriority);
+            }
+        }
+        return Status.CANCEL_STATUS;
+    }
+
+    public void start(final RemotingLoginDialogLocal loginDialog) {
+        // hide login dialog and start connection dialog
+        loginDialog.setMessage(null);
+        loginDialog.hide(true);
+
+        ProgressMonitorDialog dialog = new ProgressMonitorDialog(AbstractUtility.getShell());
+
+        try {
+            dialog.run(true, true, new IRunnableWithProgress() {
+                @Override
+                public void run(final IProgressMonitor monitor) {
+                    try {
+                        monitor.beginTask(getConnectionMessage(), 7);
+
+                        // check if database is up and running
+                        checkDatabaseReachable(monitor);
+
+                        // check if the datasource actually holds data
+                        checkIsNonEmptyCdmDatabase(monitor);
+
+                        if (dbSchemaValidation != DbSchemaValidation.CREATE) {
+                            // if we do not create the datasource, we want to check if the
+                            // datasource is compatible with this editor
+                            checkDbSchemaVersionCompatibility(monitor);
+                        }
+
+                        // we are done with our low level checking and will free resources now
+                        cdmSource.closeOpenConnections();
+
+                        display.syncExec(new Runnable() {
+                            @Override
+                            public void run() {
+                                // close the current context
+                                CdmStore.close(monitor, false);
+                            }
+                        });
+
+                        ICdmRepository applicationController = null;
+
+                        if (!monitor.isCanceled()) {
+                            CdmProgressMonitorAdapter subprogressMonitor = CdmProgressMonitorAdapter
+                                    .CreateSubMonitor(monitor, 3);
+                            // genrerate new application controller
+                            applicationController = getApplicationController(cdmSource,subprogressMonitor);
+                        }
+
+                        if (!monitor.isCanceled()) {
+                            CdmStoreLocal.setInstance(applicationController, cdmSource);
+                            CdmStoreLocal.getTermManager().reset();
+                            monitor.subTask(Messages.CdmStoreConnector_AUTHENTICATING_USER);
+                            display.syncExec(()-> {
+                                try {
+                                    // create new security context
+                                    CdmStore.getLoginManager().doAuthenticate(loginDialog.getUsername(), loginDialog.getPassword());
+                                    //loginDialog.onComplete();
+                                    CdmStore.getContextManager().notifyContextStart();
+                                    loginDialog.onComplete();
+                                    //TODO AM: is this necessary to be done on display thread?
+                                    new TermLoader().unloadAllTerms();
+                                    Rank.initDefaultTerms();
+                                    NomenclaturalStatusType.initDefaultTerms();
+                                    Language.getDefaultLanguage();
+                                } catch(CdmAuthenticationException cae) {
+                                    loginDialog.hide(false);
+                                    loginDialog.setMessage(LoginManager.INCORRECT_CREDENTIALS_MESSAGE);
+                                }
+                            });
+                        } else {
+                            throw new RuntimeException("Login cancelled");
+                        }
+                    } finally {
+                        monitor.done();
+                    }
+                }
+            });
+        } catch (InvocationTargetException e) {
+            loginDialog.hide(false);
+            loginDialog.setMessage(e.getMessage());
+        } catch (InterruptedException e) {
+            loginDialog.hide(false);
+            loginDialog.setMessage(e.getMessage());
+        }
+    }
+
+    private ICdmRepository getApplicationController(ICdmSource cdmSource, CdmProgressMonitorAdapter subprogressMonitor) {
+        if(cdmSource instanceof ICdmDataSource) {
+            return  CdmApplicationController.NewInstance(applicationContextBean,
+                    (ICdmDataSource)cdmSource,
+                    dbSchemaValidation,
+                    false,
+                    subprogressMonitor);
+        } else if(cdmSource instanceof ICdmRemoteSource) {
+            return CdmApplicationRemoteController.NewInstance((ICdmRemoteSource)cdmSource,
+                    subprogressMonitor,
+                    null);
+        } else {
+            throw new UnsupportedOperationException("Cannot create application controller for " + cdmSource.getName());
+        }
+    }
+
+    private String getConnectionMessage() {
+        return cdmSource.getConnectionMessage();
+    }
+
+    private void checkDbSchemaVersionCompatibility(IProgressMonitor monitor) {
+        monitor.subTask(Messages.CdmStoreConnector_CHECK_IF_EDITOR_IS_COMPATIBLE);
+        String dbSchemaVersion;
+
+        String message = null;
+        try {
+            dbSchemaVersion = cdmSource.getDbSchemaVersion();
+            // we assume that empty dbSchemaVersion means an empty database and
+            // skip version checking
+
+            if(dbSchemaVersion != null) {
+                int compareVersion = CdmMetaData.compareVersion(dbSchemaVersion, CdmMetaData.getDbSchemaVersion(), 3, null);
+                // if the datasource version is greater than the taxeditor compatible version then the taxeditor needs to
+                // be updated else the datasource needs to be updated
+                if(compareVersion > 0) {
+                    message = Messages.CdmStoreConnector_UPDATE_EDITOR_OR_CHOOSE_COMPATIBLE_DATASOURCE;
+                } else if (compareVersion < 0) {
+                    message = Messages.CdmStoreConnector_UPDATE_DATASOUREC_OR_CHOOSE_NEW_DATASOURCE;
+                }
+            }
+            monitor.worked(1);
+        } catch (CdmSourceException e) {
+            //
+        }
+
+        if (message != null) {
+            // Show an error message
+            MessagingUtils
+            .messageDialog(
+                    Messages.CdmStoreConnector_COMPATIBILITY_CHECK_FAILED,
+                    this,
+                    String.format(Messages.CdmStoreConnector_SCHEME_NOT_COMPATIBLE, cdmSource, message),
+                            null);
+
+            monitor.setCanceled(true);
+        }
+    }
+
+    private void checkIsNonEmptyCdmDatabase(IProgressMonitor monitor) {
+        monitor.subTask(Messages.CdmStoreConnector_CHECK_IF_NON_EMPTY);
+        boolean isDbEmpty = false;
+        try {
+            isDbEmpty = cdmSource.isDbEmpty();
+        } catch (CdmSourceException e) {
+            isDbEmpty = true;
+        }
+        if(isDbEmpty) {
+            dbSchemaValidation = DbSchemaValidation.CREATE;
+        }
+    }
+
+    private boolean causeIsCancelationExceptionRecursive(Throwable throwable){
+        if(throwable == null){
+            return false;
+        }else if(throwable instanceof CancellationException){
+            return true;
+        }else{
+            return causeIsCancelationExceptionRecursive(throwable.getCause());
+        }
+    }
+
+    private void checkDatabaseReachable(IProgressMonitor monitor) {
+        try {
+            monitor.subTask(Messages.CdmStoreConnector_CHECK_IF_REACHABLE);
+            cdmSource.checkConnection();
+            monitor.worked(1);
+        } catch (CdmSourceException e) {
+            MessagingUtils.messageDialog(Messages.CdmStoreConnector_COULD_NOT_CONNECT_TO_CHOSEN_DATASOURCE,
+                    this, Messages.CdmStoreConnector_REASON + e.getMessage(), e);
+            monitor.setCanceled(true);
+        }
+    }
+}