-// $Id$
/**
* Copyright (C) 2007 EDIT
- * European Distributed Institute of Taxonomy
+ * 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.store;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.concurrent.CancellationException;
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.database.DatabaseTypeEnum;
+import eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration;
+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.DefinedTermBase;
import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
-import eu.etaxonomy.cdm.model.metadata.CdmMetaData.MetaDataPropertyName;
+import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
+import eu.etaxonomy.cdm.model.name.Rank;
+import eu.etaxonomy.taxeditor.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.internal.TaxeditorStorePlugin;
import eu.etaxonomy.taxeditor.ui.dialog.LoginDialog;
+import eu.etaxonomy.taxeditor.ui.dialog.RemotingLoginDialog;
import eu.etaxonomy.taxeditor.view.datasource.CdmDataSourceViewPart;
+
/**
* @author n.hoffmann
* @created Dec 8, 2010
* @version 1.0
*/
class CdmStoreConnector extends Job {
- private final Display display;
- private final ICdmDataSource dataSource;
- private DbSchemaValidation dbSchemaValidation;
- private final Resource applicationContextBean;
-
- /**
- * @param datasource
- * @param dbSchemaValidation
- * @param applicationContextBean
- */
- public CdmStoreConnector(Display display, ICdmDataSource datasource,
- DbSchemaValidation dbSchemaValidation,
- Resource applicationContextBean) {
- super("Connecting to datasource: " + datasource);
- this.display = display;
- this.dataSource = datasource;
- 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
- dataSource.closeOpenConnections();
-
- if (!monitor.isCanceled()) {
- CdmStore.close(monitor);
- }
-
- CdmApplicationRemoteController applicationController = null;
-
- if (!monitor.isCanceled()) {
- CdmProgressMonitorAdapter subprogressMonitor = CdmProgressMonitorAdapter
- .CreateSubMonitor(monitor, 7);
- // This is where we instantiate the application controller
- try {
- applicationController = CdmApplicationRemoteController.NewInstance();
- } catch (Exception e) {
- if(! causeIsCancelationExceptionRecursive(e)){
- return new Status(IStatus.ERROR, "Could not connect to CDM Store", "An error occurred while trying to connect to datasource: " + dataSource.getName(), e);
- }
- } finally {
- monitor.done();
- }
- }
-
-
-
- if (!monitor.isCanceled()) {
- CdmStore.setInstance(applicationController, dataSource);
-
- display.asyncExec(new Runnable() {
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Runnable#run()
- */
- @Override
- public void run() {
- authenticate();
-
- startContext();
- }
- });
-
- StoreUtil.info("Application context initialized.");
- return Status.OK_STATUS;
- } else {
- // Show datasource view if not shown yet
- display.asyncExec(new Runnable() {
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Runnable#run()
- */
- @Override
- public void run() {
- StoreUtil.showView(CdmDataSourceViewPart.ID);
- }
- });
- return Status.CANCEL_STATUS;
- }
-
- }
-
- private void authenticate() {
- LoginDialog loginDialog = new LoginDialog(StoreUtil.getShell());
- loginDialog.open();
- }
-
- private void startContext() {
- CdmStore.getContextManager().notifyContextStart();
- }
-
- /**
- * @return
- */
- private String getConnectionMessage() {
- String message = "";
- if (dataSource.getDatabaseType().equals(DatabaseTypeEnum.H2)) {
- message = " local CDM Store ";
- } else {
- message = " CDM Community Store ";
- }
- message += "'" + dataSource.getName() + "'";
-
- message = "Connecting to" + message + ".";
-
- return message;
- }
-
- /**
- * @return
- * @throws SQLException
- */
- private void checkDbSchemaVersionCompatibility(IProgressMonitor monitor) {
- monitor.subTask("Checking if datasource is compatible with this editor.");
- String dbSchemaVersion;
- boolean result = false;
- try {
- dbSchemaVersion = (String) dataSource
- .getSingleValue(MetaDataPropertyName.DB_SCHEMA_VERSION
- .getSqlQuery());
- // we assume that empty dbSchemaVersion means an empty database and
- // skip version checking
- result = dbSchemaVersion == null ? true : CdmMetaData
- .isDbSchemaVersionCompatible(dbSchemaVersion);
- monitor.worked(1);
- } catch (SQLException e) {
- //
- }
-
- if (!result) {
- // Show an error message
- StoreUtil
- .errorDialog(
- "DatabaseCompatibilityCheck failed",
- this,
- "The database schema for the chosen "
- + "datasource '"
- + dataSource
- + "' \n is not valid for this version of the taxonomic editor. \n"
- + "Please update the chosen datasource or choose a new data source to connect to in the Datasource View.",
- null);
-
- monitor.setCanceled(true);
- }
-
- }
-
- private void checkIsNonEmptyCdmDatabase(IProgressMonitor monitor) {
- monitor.subTask("Checking if datasource is a non empty CDM database.");
-
- try {
- dataSource.getSingleValue(MetaDataPropertyName.DB_SCHEMA_VERSION
- .getSqlQuery());
- } catch (SQLException e1) {
- 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("Checking if datasource is reachable.");
- dataSource.testConnection();
- monitor.worked(1);
- } catch (ClassNotFoundException e) {
- StoreUtil.errorDialog("Could not connect to chosen datasource",
- this, "Reason: " + e.getMessage(), e);
- monitor.setCanceled(true);
- } catch (SQLException e) {
- StoreUtil.errorDialog("Could not connect to chosen datasource",
- this, "Reason: " + e.getMessage(), e);
- monitor.setCanceled(true);
- }
-
- }
+ private final Display display;
+ private final ICdmSource cdmSource;
+ private DbSchemaValidation dbSchemaValidation;
+ private final Resource applicationContextBean;
+
+
+ /**
+ * @param datasource
+ * @param dbSchemaValidation
+ * @param applicationContextBean
+ */
+ public CdmStoreConnector(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);
+ }
+
+ ICdmApplicationConfiguration 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);
+ CdmDataSourceViewPart dataSourceView = (CdmDataSourceViewPart) AbstractUtility.getView("eu.etaxonomy.taxeditor.view.datasource", false);
+ if(dataSourceView!=null){
+ dataSourceView.refresh();
+ }
+ return Status.OK_STATUS;
+ } catch (Exception e) {
+ if(! causeIsCancelationExceptionRecursive(e)){
+ MessagingUtils.errorDialog(Messages.CdmStoreConnector_COULD_NOT_CREATE_DATAMODEL, CdmStoreConnector.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 RemotingLoginDialog loginDialog) {
+ // hide login dialog and start connection dialog
+ loginDialog.setMessage(null);
+ loginDialog.hide(true);
+
+
+ ProgressMonitorDialog dialog = new ProgressMonitorDialog(StoreUtil.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() {
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Runnable#run()
+ */
+ @Override
+ public void run() {
+ // close the current context
+ CdmStore.close(monitor, false);
+ }
+ });
+
+ ICdmApplicationConfiguration applicationController = null;
+
+ if (!monitor.isCanceled()) {
+ CdmProgressMonitorAdapter subprogressMonitor = CdmProgressMonitorAdapter
+ .CreateSubMonitor(monitor, 3);
+ // genrerate new application controller
+ applicationController = getApplicationController(cdmSource,subprogressMonitor);
+ }
+
+
+ if (!monitor.isCanceled()) {
+ CdmStore.setInstance(applicationController, cdmSource);
+ monitor.subTask(Messages.CdmStoreConnector_AUTHENTICATING_USER);
+ display.syncExec(new Runnable() {
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Runnable#run()
+ */
+ @Override
+ public void run() {
+
+ try {
+ // create new security context
+ CdmStore.getLoginManager().doAuthenticate(loginDialog.getUsername(), loginDialog.getPassword());
+ loginDialog.onComplete();
+ CdmStore.getContextManager().notifyContextStart();
+ getInstance(Rank.class).resetTerms();
+ getInstance(NomenclaturalStatusType.class).resetTerms();
+ Rank.initDefaultTerms();
+ NomenclaturalStatusType.initDefaultTerms();
+ } catch(CdmAuthenticationException cae) {
+ loginDialog.hide(false);
+ loginDialog.setMessage(cae.getMessage());
+ }
+
+ }
+ });
+ } 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());
+ }
+ }
+
+ /**
+ * Returns a new instance for the given class by using the default constructor.
+ * The constructor must be declared but can be unaccessible (e.g. private)
+ * @param termClass
+ * @return
+ */
+ private <T extends DefinedTermBase> T getInstance(Class<? extends DefinedTermBase> termClass) {
+ try {
+ Constructor<T> c = ((Class<T>)termClass).getDeclaredConstructor();
+ c.setAccessible(true);
+ T termInstance = c.newInstance();
+ return termInstance;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private ICdmApplicationConfiguration 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 void authenticate() {
+ LoginDialog saloginDialog = new LoginDialog(StoreUtil.getShell());
+ saloginDialog.open();
+ }
+
+ private void startContext() {
+ CdmStore.getContextManager().notifyContextStart();
+ }
+
+ /**
+ * @return
+ */
+ private String getConnectionMessage() {
+ return cdmSource.getConnectionMessage();
+ }
+
+ /**
+ * @return
+ * @throws SQLException
+ */
+ 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);
+ }
+ }
+
+
}