2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.taxeditor
.store
;
12 import java
.lang
.reflect
.InvocationTargetException
;
13 import java
.sql
.DatabaseMetaData
;
14 import java
.sql
.ResultSet
;
15 import java
.sql
.SQLException
;
16 import java
.util
.ArrayList
;
17 import java
.util
.List
;
19 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
20 import org
.eclipse
.jface
.dialogs
.ProgressMonitorDialog
;
21 import org
.eclipse
.jface
.operation
.IRunnableWithProgress
;
22 import org
.springframework
.core
.io
.ClassPathResource
;
23 import org
.springframework
.core
.io
.Resource
;
24 import org
.springframework
.security
.authentication
.ProviderManager
;
26 import eu
.etaxonomy
.cdm
.api
.application
.CdmApplicationController
;
27 import eu
.etaxonomy
.cdm
.api
.conversation
.ConversationHolder
;
28 import eu
.etaxonomy
.cdm
.api
.service
.IAgentService
;
29 import eu
.etaxonomy
.cdm
.api
.service
.IClassificationService
;
30 import eu
.etaxonomy
.cdm
.api
.service
.ICollectionService
;
31 import eu
.etaxonomy
.cdm
.api
.service
.ICommonService
;
32 import eu
.etaxonomy
.cdm
.api
.service
.IFeatureNodeService
;
33 import eu
.etaxonomy
.cdm
.api
.service
.IFeatureTreeService
;
34 import eu
.etaxonomy
.cdm
.api
.service
.ILocationService
;
35 import eu
.etaxonomy
.cdm
.api
.service
.IMediaService
;
36 import eu
.etaxonomy
.cdm
.api
.service
.INameService
;
37 import eu
.etaxonomy
.cdm
.api
.service
.IOccurrenceService
;
38 import eu
.etaxonomy
.cdm
.api
.service
.IReferenceService
;
39 import eu
.etaxonomy
.cdm
.api
.service
.ITaxonNodeService
;
40 import eu
.etaxonomy
.cdm
.api
.service
.ITaxonService
;
41 import eu
.etaxonomy
.cdm
.api
.service
.ITermService
;
42 import eu
.etaxonomy
.cdm
.api
.service
.IUserService
;
43 import eu
.etaxonomy
.cdm
.api
.service
.IVocabularyService
;
44 import eu
.etaxonomy
.cdm
.api
.service
.config
.IIdentifiableEntityServiceConfigurator
;
45 import eu
.etaxonomy
.cdm
.api
.service
.config
.ITaxonServiceConfigurator
;
46 import eu
.etaxonomy
.cdm
.database
.DatabaseTypeEnum
;
47 import eu
.etaxonomy
.cdm
.database
.DbSchemaValidation
;
48 import eu
.etaxonomy
.cdm
.database
.ICdmDataSource
;
49 import eu
.etaxonomy
.cdm
.ext
.geo
.IEditGeoService
;
50 import eu
.etaxonomy
.cdm
.model
.agent
.AgentBase
;
51 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
52 import eu
.etaxonomy
.cdm
.model
.common
.CdmMetaData
;
53 import eu
.etaxonomy
.cdm
.model
.common
.CdmMetaData
.MetaDataPropertyName
;
54 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
55 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
56 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
57 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
58 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
59 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
60 import eu
.etaxonomy
.taxeditor
.datasource
.CdmDataSourceRepository
;
61 import eu
.etaxonomy
.taxeditor
.datasource
.view
.CdmDataSourceViewPart
;
62 import eu
.etaxonomy
.taxeditor
.io
.ExportHandler
;
63 import eu
.etaxonomy
.taxeditor
.io
.ImportHandler
;
64 import eu
.etaxonomy
.taxeditor
.model
.CdmProgressMonitorAdapter
;
65 import eu
.etaxonomy
.taxeditor
.store
.internal
.TaxeditorStorePlugin
;
68 * This implementation of ICdmDataRepository depends on hibernate sessions to store the data correctly
69 * for the current session. No state is held in this class.
71 * Only methods that either get or manipulate data are exposed here. So this class acts as a facade
72 * for the methods in cdmlib-service.
78 public class CdmStore
{
80 private static class CdmDataStoreConnector
implements IRunnableWithProgress
{
81 private ICdmDataSource dataSource
;
82 private DbSchemaValidation dbSchemaValidation
;
83 private Resource applicationContextBean
;
87 * @param dbSchemaValidation
88 * @param applicationContextBean
90 public CdmDataStoreConnector(ICdmDataSource datasource
,
91 DbSchemaValidation dbSchemaValidation
,
92 Resource applicationContextBean
) {
93 this.dataSource
= datasource
;
94 this.dbSchemaValidation
= dbSchemaValidation
;
95 this.applicationContextBean
= applicationContextBean
;
99 public void run(final IProgressMonitor monitor
) {
101 monitor
.beginTask(getConnectionMessage(), 10);
103 // check if database is up and running
104 checkDatabaseReachable(monitor
);
106 if(! monitor
.isCanceled()){
107 // check if the datasource actually holds data
108 checkDatabaseNotEmpty(monitor
);
111 if(dbSchemaValidation
!= DbSchemaValidation
.CREATE
&& ! monitor
.isCanceled()){
112 // if we do not create the datasource, we want to check if the datasource is compatible with this editor
113 checkDbSchemaVersionCompatibility(monitor
);
116 // we are done with our low level checking and will free resources now
117 dataSource
.closeOpenConnections();
119 if(! monitor
.isCanceled()){
123 CdmApplicationController applicationController
= null;
125 if(! monitor
.isCanceled()){
126 CdmProgressMonitorAdapter subprogressMonitor
= CdmProgressMonitorAdapter
.CreateSubMonitor(monitor
, 5);
127 // This is where we instantiate the application controller
129 applicationController
= CdmApplicationController
.NewInstance(applicationContextBean
, dataSource
, dbSchemaValidation
, false, subprogressMonitor
);
131 StoreUtil
.error(this.getClass(), e
.getMessage(), e
);
135 if(! monitor
.isCanceled()){
136 setInstance(applicationController
, dataSource
);
138 getContextManager().notifyContextStart(monitor
);
141 StoreUtil
.info("Application context initialized.");
143 // Show datasource view if not shown yet
144 StoreUtil
.showView(CdmDataSourceViewPart
.ID
);
153 private String
getConnectionMessage() {
155 if(dataSource
.getDatabaseType().equals(DatabaseTypeEnum
.H2
)){
156 message
= " local CDM Store ";
158 message
= " CDM Community Store ";
160 message
+= "'" + dataSource
.getName() + "'";
162 message
= "Connecting to" + message
+ ".";
169 * @throws SQLException
171 private void checkDbSchemaVersionCompatibility(IProgressMonitor monitor
) {
172 monitor
.subTask("Checking if datasource is compatible with this editor.");
173 String dbSchemaVersion
;
174 boolean result
= false;
176 dbSchemaVersion
= (String
) dataSource
.getSingleValue(MetaDataPropertyName
.DB_SCHEMA_VERSION
.getSqlQuery());
177 // we assume that empty dbSchemaVersion means an empty database and skip version checking
178 result
= dbSchemaVersion
== null ?
true : CdmMetaData
.isDbSchemaVersionCompatible(dbSchemaVersion
);
180 } catch (SQLException e
) {
185 // Show an error message
186 StoreUtil
.errorDialog("DatabaseCompatibilityCheck failed", this, "The database schema for the chosen " +
187 "datasource '" + dataSource
+ "' \n is not valid for this version of the taxonomic editor. \n" +
188 "Please update the chosen datasource or choose a new data source to connect to in the Datasource View.", null);
190 monitor
.setCanceled(true);
195 private void checkDatabaseNotEmpty(IProgressMonitor monitor
) {
196 monitor
.subTask("Checking if datasource is not empty.");
197 DatabaseMetaData metaData
= dataSource
.getMetaData();
198 if(metaData
!= null){
200 ResultSet resultSet
= metaData
.getTables(dataSource
.getDatabase(), null, null, null);
201 if (resultSet
!= null){
202 if(! resultSet
.next()){
203 dbSchemaValidation
= DbSchemaValidation
.CREATE
;
208 } catch (SQLException e
) {
209 StoreUtil
.errorDialog("Error while trying to retrieve database metadata", this, "Something is utterly wrong with your database.", e
);
210 StoreUtil
.error(this.getClass(), e
.getMessage(), e
);
214 monitor
.setCanceled(true);
217 private void checkDatabaseReachable(IProgressMonitor monitor
){
219 monitor
.subTask("Checking if datasource is reachable.");
220 dataSource
.testConnection();
222 } catch (ClassNotFoundException e
) {
223 StoreUtil
.errorDialog("Could not connect to chosen datasource", this, "Reason: " + e
.getMessage(), e
);
224 monitor
.setCanceled(true);
225 } catch (SQLException e
) {
226 StoreUtil
.errorDialog("Could not connect to chosen datasource", this, "Reason: " + e
.getMessage(), e
);
227 monitor
.setCanceled(true);
235 private static final Resource DEFAULT_APPLICATION_CONTEXT
= new ClassPathResource("/eu/etaxonomy/cdm/editorApplicationContext.xml", TaxeditorStorePlugin
.class);
236 private static final DbSchemaValidation DEFAULT_DB_SCHEMA_VALIDATION
= DbSchemaValidation
.VALIDATE
;
238 private static CdmStore instance
;
240 private CdmApplicationController applicationController
;
242 private static LoginManager loginManager
;
244 private static ImportHandler importHandler
;
246 private static ExportHandler exportHandler
;
248 private static ContextManager contextManager
;
250 private Language language
;
252 private ICdmDataSource cdmDatasource
;
254 private boolean isConnected
;
259 * @return a {@link eu.etaxonomy.taxeditor.store.CdmStore} object.
261 protected static CdmStore
getDefault(){
262 if(instance
!= null && instance
.isConnected
){
264 }else if(instance
== null || !instance
.isConnected
){
266 StoreUtil
.warningDialog("Application is not connected to a datastore", instance
, "The requested operation is only available when " +
267 "connected to a datasource. You may choose a datasource to connect to or create a new one in the datasource view.");
269 StoreUtil
.showView(CdmDataSourceViewPart
.ID
);
276 * Initialize the with the last edited datasource
278 public static void connect() {
280 ICdmDataSource datasource
= CdmDataSourceRepository
.getCurrentDataSource();
286 * Initialize with a specific datasource
288 * @param datasource a {@link eu.etaxonomy.cdm.database.ICdmDataSource} object.
290 public static void connect(ICdmDataSource datasource
) {
291 connect(datasource
, DEFAULT_DB_SCHEMA_VALIDATION
, DEFAULT_APPLICATION_CONTEXT
);
295 * Initialize and provide
298 * @param dbSchemaValidation
299 * @param applicationContextBean
301 private static void connect(final ICdmDataSource datasource
, final DbSchemaValidation dbSchemaValidation
, final Resource applicationContextBean
){
302 StoreUtil
.info("Connecting to datasource: " + datasource
);
305 ProgressMonitorDialog dialog
= new ProgressMonitorDialog(StoreUtil
.getShell());
306 dialog
.run(false, true, new CdmDataStoreConnector(datasource
, dbSchemaValidation
, applicationContextBean
));
307 } catch (InvocationTargetException e
) {
308 StoreUtil
.error(CdmStore
.class, e
);
309 } catch (InterruptedException e
) {
310 StoreUtil
.error(CdmStore
.class, e
);
318 * Closes the current application context
320 * @param monitor a {@link org.eclipse.core.runtime.IProgressMonitor} object.
322 public static void close(IProgressMonitor monitor
){
323 getContextManager().notifyContextAboutToStop(monitor
);
324 if((monitor
== null || (!monitor
.isCanceled()) && isActive() && StoreUtil
.closeAll())){
325 getContextManager().notifyContextStop(monitor
);
326 if(instance
.getApplicationController() != null){
327 instance
.getApplicationController().close();
333 private void close() {
335 cdmDatasource
= null;
338 private static void setInstance(CdmApplicationController applicationController
, ICdmDataSource dataSource
){
339 instance
= new CdmStore(applicationController
, dataSource
);
342 private CdmStore(CdmApplicationController applicationController
, ICdmDataSource dataSource
){
343 this.applicationController
= applicationController
;
344 this.cdmDatasource
= dataSource
;
349 * All calls to the datastore require
353 private CdmApplicationController
getApplicationController(){
355 return applicationController
;
357 StoreUtil
.error(CdmStore
.class, e
);
363 * <p>getCurrentApplicationController</p>
365 * @return a {@link eu.etaxonomy.cdm.api.application.CdmApplicationController} object.
367 public static CdmApplicationController
getCurrentApplicationController(){
368 if(getDefault() != null){
369 return getDefault().getApplicationController();
379 * Creates a new conversation, binds resources to the conversation and
380 * start a transaction for this conversation.
382 * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder} object.
384 public static ConversationHolder
createConversation() {
385 ConversationHolder conversation
= getCurrentApplicationController().NewConversation();
387 conversation
.startTransaction();
396 * <p>getTaxonService</p>
398 * @return a {@link eu.etaxonomy.cdm.api.service.ITaxonService} object.
400 public static ITaxonService
getTaxonService(){ return getCurrentApplicationController().getTaxonService();}
403 * <p>getTaxonTreeService</p>
405 * @return a {@link eu.etaxonomy.cdm.api.service.ITaxonTreeService} object.
407 public static IClassificationService
getClassificationService() { return getCurrentApplicationController().getClassificationService();}
410 * <p>getTaxonNodeService</p>
412 * @return a {@link eu.etaxonomy.cdm.api.service.ITaxonNodeService} object.
414 public static ITaxonNodeService
getTaxonNodeService() { return getCurrentApplicationController().getTaxonNodeService();}
417 * <p>getNameService</p>
419 * @return a {@link eu.etaxonomy.cdm.api.service.INameService} object.
421 public static INameService
getNameService(){ return getCurrentApplicationController().getNameService();}
424 * <p>getReferenceService</p>
426 * @return a {@link eu.etaxonomy.cdm.api.service.IReferenceService} object.
428 public static IReferenceService
getReferenceService(){ return getCurrentApplicationController().getReferenceService();}
431 * <p>getLocationService</p>
433 * @return a {@link eu.etaxonomy.cdm.api.service.ILocationService} object.
435 public static ILocationService
getLocationService(){ return getCurrentApplicationController().getLocationService();}
438 * <p>getAuthenticationManager</p>
440 * @return a {@link org.springframework.security.authentication.ProviderManager} object.
442 public static ProviderManager
getAuthenticationManager() { return getCurrentApplicationController().getAuthenticationManager();}
445 * <p>getUserService</p>
447 * @return a {@link eu.etaxonomy.cdm.api.service.IUserService} object.
449 public static IUserService
getUserService() { return getCurrentApplicationController().getUserService(); }
452 * <p>getCommonService</p>
454 * @return a {@link eu.etaxonomy.cdm.api.service.ICommonService} object.
456 public static ICommonService
getCommonService() { return getCurrentApplicationController().getCommonService(); }
459 * <p>getAgentService</p>
461 * @return a {@link eu.etaxonomy.cdm.api.service.IAgentService} object.
463 public static IAgentService
getAgentService() { return getCurrentApplicationController().getAgentService(); }
466 * <p>getTermService</p>
468 * @return a {@link eu.etaxonomy.cdm.api.service.ITermService} object.
470 public static ITermService
getTermService() { return getCurrentApplicationController() != null ?
getCurrentApplicationController().getTermService() : null; }
473 * <p>getVocabularyService</p>
475 * @return a {@link eu.etaxonomy.cdm.api.service.IVocabularyService} object.
477 public static IVocabularyService
getVocabularyService() { return getCurrentApplicationController().getVocabularyService(); }
480 * <p>getMediaService</p>
482 * @return a {@link eu.etaxonomy.cdm.api.service.IMediaService} object.
484 public static IMediaService
getMediaService() { return getCurrentApplicationController().getMediaService(); }
487 * <p>getOccurrenceService</p>
489 * @return a {@link eu.etaxonomy.cdm.api.service.IOccurrenceService} object.
491 public static IOccurrenceService
getOccurrenceService() { return getCurrentApplicationController().getOccurrenceService(); }
494 * <p>getFeatureTreeService</p>
496 * @return a {@link eu.etaxonomy.cdm.api.service.IFeatureTreeService} object.
498 public static IFeatureTreeService
getFeatureTreeService() { return getCurrentApplicationController().getFeatureTreeService(); }
501 * <p>getFeatureNodeService</p>
503 * @return a {@link eu.etaxonomy.cdm.api.service.IFeatureNodeService} object.
505 public static IFeatureNodeService
getFeatureNodeService() { return getCurrentApplicationController().getFeatureNodeService(); }
508 * <p>getCollectionService</p>
510 * @return a {@link eu.etaxonomy.cdm.api.service.ICollectionService} object.
512 public static ICollectionService
getCollectionService() { return getCurrentApplicationController().getCollectionService(); }
515 * <p>getGeoService</p>
517 * @return a {@link eu.etaxonomy.cdm.ext.geo.IEditGeoService} object.
519 public static IEditGeoService
getGeoService(){
520 return (IEditGeoService
) getCurrentApplicationController().getBean("editGeoService");
524 * METHODS TO FIND ENTITIES
530 * @param configurator a {@link eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator} object.
531 * @return a {@link java.util.List} object.
533 public static List
<TaxonNameBase
> findNames(IIdentifiableEntityServiceConfigurator configurator
){
534 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
535 // unfortunately this is not consistently implemented in the library.
536 // FIXME use proper method once it is implemented in the library
537 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
539 return getNameService().getNamesByName(titleSearchString
);
543 * <p>findTaxaAndNames</p>
545 * @param configurator a {@link eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator} object.
546 * @return a {@link java.util.List} object.
548 public static List
<IdentifiableEntity
> findTaxaAndNames(ITaxonServiceConfigurator configurator
){
549 return getTaxonService().findTaxaAndNames(configurator
).getRecords();
553 * <p>findReferences</p>
555 * @param configurator a {@link eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator} object.
556 * @return a {@link java.util.List} object.
558 public static List
<Reference
> findReferences(IIdentifiableEntityServiceConfigurator configurator
){
559 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
560 // unfortunately this is not consistently implemented in the library.
561 // FIXME use proper method once it is implemented in the library
562 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
564 return getReferenceService().findByTitle(null, titleSearchString
, null, null, null, null, null, null).getRecords();
570 * @param configurator a {@link eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator} object.
571 * @return a {@link java.util.List} object.
573 public static List
<AgentBase
> findAgents(IIdentifiableEntityServiceConfigurator configurator
){
574 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
575 // unfortunately this is not consistently implemented in the library.
576 // FIXME use proper method once it is implemented in the library
577 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
579 return getAgentService().findByTitle(null, titleSearchString
, null, null, null, null, null, null).getRecords();
583 * <p>findTeamOrPersons</p>
585 * @param configurator a {@link eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator} object.
586 * @return a {@link java.util.List} object.
588 public static List
<TeamOrPersonBase
> findTeamOrPersons(IIdentifiableEntityServiceConfigurator configurator
){
589 // TODO move this to cdmlib
590 List
<TeamOrPersonBase
> result
= new ArrayList
<TeamOrPersonBase
>();
591 for (AgentBase agent
: findAgents(configurator
)) {
592 if (agent
instanceof TeamOrPersonBase
) {
593 result
.add((TeamOrPersonBase
) agent
);
600 * <p>findOccurrences</p>
602 * @param configurator a {@link eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator} object.
603 * @return a {@link java.util.List} object.
605 public static List
<SpecimenOrObservationBase
> findOccurrences(IIdentifiableEntityServiceConfigurator configurator
){
606 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
607 // unfortunately this is not consistently implemented in the library.
608 // FIXME use proper method once it is implemented in the library
609 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
611 return getOccurrenceService().findByTitle(SpecimenOrObservationBase
.class, titleSearchString
, null, null, null, null, null, null).getRecords();
619 * <p>getDefaultLanguage</p>
621 * @return a {@link eu.etaxonomy.cdm.model.common.Language} object.
623 public static Language
getDefaultLanguage(){
624 if(getDefault().getLanguage() == null){
625 getDefault().setLanguage(Language
.DEFAULT());
627 return getDefault().getLanguage();
631 * <p>setDefaultLanguage</p>
633 * @param language a {@link eu.etaxonomy.cdm.model.common.Language} object.
635 public static void setDefaultLanguage(Language language
){
636 getDefault().setLanguage(language
);
640 * @return the language
642 private Language
getLanguage() {
647 * @param language the language to set
649 private void setLanguage(Language language
) {
650 this.language
= language
;
658 * <p>Getter for the field <code>loginManager</code>.</p>
660 * @return a {@link eu.etaxonomy.taxeditor.store.LoginManager} object.
662 public static LoginManager
getLoginManager(){
663 if(loginManager
== null){
664 loginManager
= new LoginManager();
670 * <p>Getter for the field <code>contextManager</code>.</p>
672 * @return a {@link eu.etaxonomy.taxeditor.store.ContextManager} object.
674 public static synchronized ContextManager
getContextManager(){
675 if(contextManager
== null){
676 contextManager
= new ContextManager();
678 return contextManager
;
682 * IMPORT/EXPORT FACTORIES
686 * <p>Getter for the field <code>importHandler</code>.</p>
688 * @return a {@link eu.etaxonomy.taxeditor.io.ImportHandler} object.
690 public static synchronized ImportHandler
getImportHandler(){
691 if(importHandler
== null){
692 importHandler
= ImportHandler
.NewInstance(getCurrentApplicationController());
694 return importHandler
;
698 * <p>Getter for the field <code>exportHandler</code>.</p>
700 * @return a {@link eu.etaxonomy.taxeditor.io.ExportHandler} object.
702 public static synchronized ExportHandler
getExportHandler(){
703 if(exportHandler
== null){
704 exportHandler
= ExportHandler
.NewInstance(getCurrentApplicationController());
706 return exportHandler
;
710 * Whether this CdmStore is currently connected to a datasource
714 public static boolean isActive(){
715 return instance
!= null && instance
.isConnected
;
719 * <p>getDataSource</p>
721 * @return a {@link eu.etaxonomy.cdm.database.ICdmDataSource} object.
723 public static ICdmDataSource
getDataSource(){
725 return instance
.getDatasource();
733 private ICdmDataSource
getDatasource() {
734 return cdmDatasource
;
738 * <p>createDefaultClassification</p>
740 * @param conversation a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder} object.
742 public static void createDefaultClassification(ConversationHolder conversation
){
743 Classification defaultClassification
= Classification
.NewInstance("My Classification");
744 getClassificationService().saveOrUpdate(defaultClassification
);
745 conversation
.commit(true);