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
.apache
.log4j
.Logger
;
20 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
21 import org
.eclipse
.core
.runtime
.ListenerList
;
22 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
23 import org
.eclipse
.jface
.operation
.IRunnableWithProgress
;
24 import org
.springframework
.core
.io
.ClassPathResource
;
25 import org
.springframework
.core
.io
.Resource
;
26 import org
.springframework
.security
.authentication
.ProviderManager
;
28 import eu
.etaxonomy
.cdm
.api
.application
.CdmApplicationController
;
29 import eu
.etaxonomy
.cdm
.api
.conversation
.ConversationHolder
;
30 import eu
.etaxonomy
.cdm
.api
.service
.IAgentService
;
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
.ITaxonTreeService
;
42 import eu
.etaxonomy
.cdm
.api
.service
.ITermService
;
43 import eu
.etaxonomy
.cdm
.api
.service
.IUserService
;
44 import eu
.etaxonomy
.cdm
.api
.service
.IVocabularyService
;
45 import eu
.etaxonomy
.cdm
.api
.service
.config
.IIdentifiableEntityServiceConfigurator
;
46 import eu
.etaxonomy
.cdm
.api
.service
.config
.ITaxonServiceConfigurator
;
47 import eu
.etaxonomy
.cdm
.database
.DataSourceNotFoundException
;
48 import eu
.etaxonomy
.cdm
.database
.DatabaseTypeEnum
;
49 import eu
.etaxonomy
.cdm
.database
.DbSchemaValidation
;
50 import eu
.etaxonomy
.cdm
.database
.ICdmDataSource
;
51 import eu
.etaxonomy
.cdm
.ext
.geo
.IEditGeoService
;
52 import eu
.etaxonomy
.cdm
.model
.agent
.AgentBase
;
53 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
54 import eu
.etaxonomy
.cdm
.model
.common
.CdmMetaData
;
55 import eu
.etaxonomy
.cdm
.model
.common
.CdmMetaData
.MetaDataPropertyName
;
56 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
57 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
58 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
59 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
60 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceBase
;
61 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonomicTree
;
62 import eu
.etaxonomy
.taxeditor
.datasource
.CdmDataSourceRepository
;
63 import eu
.etaxonomy
.taxeditor
.datasource
.view
.CdmDataSourceViewPart
;
64 import eu
.etaxonomy
.taxeditor
.dialogs
.LoginDialog
;
65 import eu
.etaxonomy
.taxeditor
.io
.ExportHandler
;
66 import eu
.etaxonomy
.taxeditor
.io
.ImportHandler
;
67 import eu
.etaxonomy
.taxeditor
.model
.CdmProgressMonitorAdapter
;
68 import eu
.etaxonomy
.taxeditor
.model
.IContextListener
;
69 import eu
.etaxonomy
.taxeditor
.model
.IContextListener
.EventType
;
70 import eu
.etaxonomy
.taxeditor
.store
.internal
.TaxeditorStorePlugin
;
73 * This implementation of ICdmDataRepository depends on hibernate sessions to store the data correctly
74 * for the current session. No state is held in this class.
76 * Only methods that either get or manipulate data are exposed here. So this class acts as a facade
77 * for the methods in cdmlib-service.
84 public class CdmStore
{
85 private static final Logger logger
= Logger
.getLogger(CdmStore
.class);
87 private static class CdmDataStoreConnector
implements IRunnableWithProgress
{
88 private ICdmDataSource datasource
;
89 private DbSchemaValidation dbSchemaValidation
;
90 private Resource applicationContextBean
;
91 private IProgressMonitor monitor
;
95 * @param dbSchemaValidation
96 * @param applicationContextBean
98 public CdmDataStoreConnector(ICdmDataSource datasource
,
99 DbSchemaValidation dbSchemaValidation
,
100 Resource applicationContextBean
) {
101 this.datasource
= datasource
;
102 this.dbSchemaValidation
= dbSchemaValidation
;
103 this.applicationContextBean
= applicationContextBean
;
108 public void run(IProgressMonitor progressMonitor
) throws InvocationTargetException
, InterruptedException
{
110 this.monitor
= progressMonitor
!= null ? progressMonitor
: new NullProgressMonitor();
112 monitor
.beginTask(getConnectionMessage(), 10);
114 checkDatabaseReachable();
116 if(! monitor
.isCanceled()){
117 checkDatabaseNotEmpty();
120 if(! monitor
.isCanceled()){
121 checkDbSchemaVersionCompatibility();
124 // we are done with our low level checking and will free resources now
125 datasource
.closeOpenConnections();
127 if(! monitor
.isCanceled()){
128 instance
= new CdmStore(datasource
, dbSchemaValidation
, applicationContextBean
, new CdmProgressMonitorAdapter(monitor
));
129 notifyContextStart();
130 logger
.info("Application context initialized.");
132 // Show datasource view if not shown yet
133 StoreUtil
.showView(CdmDataSourceViewPart
.ID
);
144 private String
getConnectionMessage() {
146 if(datasource
.getDatabaseType().equals(DatabaseTypeEnum
.H2
)){
147 message
= " local CDM Store ";
149 message
= " CDM Community Store ";
151 message
+= "'" + datasource
.getName() + "'";
153 message
= "Establishing connection to" + message
+ ". \nThis might take while.";
160 * @throws SQLException
162 private void checkDbSchemaVersionCompatibility() {
163 String dbSchemaVersion
;
164 boolean result
= false;
166 dbSchemaVersion
= (String
) datasource
.getSingleValue(MetaDataPropertyName
.DB_SCHEMA_VERSION
.getSqlQuery());
167 // we assume that empty dbSchemaVersion means an empty database and skip version checking
168 result
= dbSchemaVersion
== null ?
true : CdmMetaData
.isDbSchemaVersionCompatible(dbSchemaVersion
);
169 } catch (SQLException e
) {
174 // Show an error message
175 StoreUtil
.errorDialog("DatabaseCompatibilityCheck failed", "The database schema for the chosen " +
176 "datasource '" + datasource
+ "' \n is not valid for this version of the taxonomic editor. \n" +
177 "Please update the chosen datasource or choose a new data source to connect to in the Datasource View.");
179 monitor
.setCanceled(true);
184 private void checkDatabaseNotEmpty() {
185 monitor
.subTask("Checking if datasource is not empty.");
186 DatabaseMetaData metaData
= datasource
.getMetaData();
187 if(metaData
!= null){
189 ResultSet resultSet
= metaData
.getTables(datasource
.getDatabase(), null, null, null);
190 if (resultSet
!= null){
191 if(resultSet
.next()){
194 if(StoreUtil
.confirmDialog("Database is empty", "Do you want to create the datasource?")){
195 dbSchemaValidation
= DbSchemaValidation
.CREATE
;
200 } catch (SQLException e
) {
201 StoreUtil
.errorDialog("Error while trying to retrieve database metadata", "Something is utterly wrong with your database.");
206 monitor
.setCanceled(true);
209 private void checkDatabaseReachable(){
211 monitor
.subTask("Checking if datasource is reachable.");
212 datasource
.testConnection();
214 } catch (DataSourceNotFoundException e
) {
215 StoreUtil
.errorDialog("Chosen Datasource is not available", "Could not connect to the chosen " +
216 "datasource '" + datasource
+ "'. Please check settings in datasources.xml. If the datasource " +
217 "is located on a remote machine, make sure you are connected to the network.");
218 monitor
.setCanceled(true);
224 private static final Resource DEFAULT_APPLICATION_CONTEXT
= new ClassPathResource("/eu/etaxonomy/cdm/editorApplicationContext.xml", TaxeditorStorePlugin
.class.getClassLoader());
225 private static final DbSchemaValidation DEFAULT_DB_SCHEMA_VALIDATION
= DbSchemaValidation
.VALIDATE
;
227 private static CdmStore instance
;
229 private CdmApplicationController applicationController
;
231 private static LoginManager loginManager
;
233 private static ImportHandler importHandler
;
235 private static ExportHandler exportHandler
;
237 private Language language
;
239 private static ListenerList contextListeners
= new ListenerList();
241 private static ICdmDataSource cdmDatasource
;
243 private boolean isConnected
;
247 * @param applicationContextBean
250 protected static CdmStore
getDefault(){
251 if(instance
!= null && instance
.isConnected
){
253 }else if(instance
== null){
258 }else if(!instance
.isConnected
){
260 StoreUtil
.warningDialog("No datasource connection", "Application is not connected to a datastore.");
262 // TODO open datasource view
269 * Initialize the with the last edited datasource
271 public static void initialize() {
273 ICdmDataSource datasource
= CdmDataSourceRepository
.getCurrentDataSource();
275 initialize(datasource
);
279 * Initialize with a specific datasource
283 public static void initialize(ICdmDataSource datasource
) {
284 initialize(datasource
, DEFAULT_DB_SCHEMA_VALIDATION
, DEFAULT_APPLICATION_CONTEXT
);
288 * Initialize and provide
291 * @param dbSchemaValidation
292 * @param applicationContextBean
294 private static void initialize(final ICdmDataSource datasource
, final DbSchemaValidation dbSchemaValidation
, final Resource applicationContextBean
){
296 logger
.error("Application context already initialized.");
300 logger
.info("Initializing application context ...");
304 CdmDataStoreConnector runnable
= new CdmDataStoreConnector(datasource
, dbSchemaValidation
, applicationContextBean
);
306 StoreUtil
.run(false, false, runnable
);
308 } catch (Exception e
){
309 StoreUtil
.errorDialog("Error trying to connect to datasource", "An error occurred while connecting to the datasource." +
310 "Please refer to the error log.");
312 throw new RuntimeException(e
);
319 * Closes the current application context
321 public static void close(){
322 notifyContextAboutToStop();
323 if(isActive() && StoreUtil
.closeAll()){
325 instance
.getApplicationController().close();
326 instance
.isConnected
= false;
327 cdmDatasource
= null;
335 private CdmStore(ICdmDataSource dataSource
, DbSchemaValidation dbSchemaValidation
, Resource applicationContextBean
, eu
.etaxonomy
.cdm
.common
.IProgressMonitor progressMonitor
) {
337 // this should be more modulized
338 applicationController
= CdmApplicationController
.NewInstance(applicationContextBean
, dataSource
, dbSchemaValidation
, false, progressMonitor
);
342 cdmDatasource
= dataSource
;
343 } catch (Exception e
) {
344 throw new RuntimeException(e
);
349 * All calls to the datastore require
353 private CdmApplicationController
getApplicationController(){
355 return applicationController
;
357 logger
.error("Exception thrown", e
);
362 public static CdmApplicationController
getCurrentApplicationController(){
363 return getDefault().getApplicationController();
371 * Creates a new conversation, binds resources to the conversation and
372 * start a transaction for this conversation.
376 public static ConversationHolder
createConversation() {
377 ConversationHolder conversation
= getDefault().getApplicationController().NewConversation();
379 conversation
.startTransaction();
387 public static ITaxonService
getTaxonService(){ return getDefault().getApplicationController().getTaxonService();}
389 public static ITaxonTreeService
getTaxonTreeService() { return getDefault().getApplicationController().getTaxonTreeService();}
391 public static ITaxonNodeService
getTaxonNodeService() { return getDefault().getApplicationController().getTaxonNodeService();}
393 public static INameService
getNameService(){ return getDefault().getApplicationController().getNameService();}
395 public static IReferenceService
getReferenceService(){ return getDefault().getApplicationController().getReferenceService();}
397 public static ILocationService
getLocationService(){ return getDefault().getApplicationController().getLocationService();}
399 public static ProviderManager
getAuthenticationManager() { return getDefault().getApplicationController().getAuthenticationManager();}
401 public static IUserService
getUserService() { return getDefault().getApplicationController().getUserService(); }
403 public static ICommonService
getCommonService() { return getDefault().getApplicationController().getCommonService(); }
405 public static IAgentService
getAgentService() { return getDefault().getApplicationController().getAgentService(); }
407 public static ITermService
getTermService() { return getDefault().getApplicationController().getTermService(); }
409 public static IVocabularyService
getVocabularyService() { return getDefault().getApplicationController().getVocabularyService(); }
411 public static IMediaService
getMediaService() { return getDefault().getApplicationController().getMediaService(); }
413 public static IOccurrenceService
getOccurrenceService() { return getDefault().getApplicationController().getOccurrenceService(); }
415 public static IFeatureTreeService
getFeatureTreeService() { return getDefault().getApplicationController().getFeatureTreeService(); }
417 public static IFeatureNodeService
getFeatureNodeService() { return getDefault().getApplicationController().getFeatureNodeService(); }
419 public static IEditGeoService
getGeoService(){
420 return (IEditGeoService
) getDefault().getApplicationController().getBean("editGeoService");
424 * METHODS TO FIND ENTITIES
429 * @param configurator
432 public static List
<TaxonNameBase
> findNames(IIdentifiableEntityServiceConfigurator configurator
){
433 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
434 // unfortunately this is not consistently implemented in the library.
435 // FIXME use proper method once it is implemented in the library
436 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
438 return getNameService().getNamesByName(titleSearchString
);
443 * @param configurator
446 public static List
<IdentifiableEntity
> findTaxaAndNames(ITaxonServiceConfigurator configurator
){
447 return getTaxonService().findTaxaAndNames(configurator
).getRecords();
452 * @param configurator
455 public static List
<ReferenceBase
> findReferences(IIdentifiableEntityServiceConfigurator configurator
){
456 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
457 // unfortunately this is not consistently implemented in the library.
458 // FIXME use proper method once it is implemented in the library
459 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
461 return getReferenceService().findByTitle(null, titleSearchString
, null, null, null, null, null, null).getRecords();
466 * @param configurator
469 public static List
<AgentBase
> findAgents(IIdentifiableEntityServiceConfigurator configurator
){
470 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
471 // unfortunately this is not consistently implemented in the library.
472 // FIXME use proper method once it is implemented in the library
473 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
475 return getAgentService().findByTitle(null, titleSearchString
, null, null, null, null, null, null).getRecords();
478 public static List
<TeamOrPersonBase
> findTeamOrPersons(IIdentifiableEntityServiceConfigurator configurator
){
479 // TODO move this to cdmlib
480 List
<TeamOrPersonBase
> result
= new ArrayList
<TeamOrPersonBase
>();
481 for (AgentBase agent
: findAgents(configurator
)) {
482 if (agent
instanceof TeamOrPersonBase
) {
483 result
.add((TeamOrPersonBase
) agent
);
489 public static List
<SpecimenOrObservationBase
> findOccurrences(IIdentifiableEntityServiceConfigurator configurator
){
490 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
491 // unfortunately this is not consistently implemented in the library.
492 // FIXME use proper method once it is implemented in the library
493 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
495 return getOccurrenceService().findByTitle(SpecimenOrObservationBase
.class, titleSearchString
, null, null, null, null, null, null).getRecords();
505 public static Language
getDefaultLanguage(){
506 if(getDefault().getLanguage() == null){
507 getDefault().setLanguage(Language
.DEFAULT());
509 return getDefault().getLanguage();
512 public static void setDefaultLanguage(Language language
){
513 getDefault().setLanguage(language
);
517 * @return the language
519 private Language
getLanguage() {
524 * @param language the language to set
526 private void setLanguage(Language language
) {
527 this.language
= language
;
537 public static LoginManager
getLoginManager(){
538 if(loginManager
== null){
539 loginManager
= new LoginManager();
544 private int authenticate(){
546 LoginDialog loginDialog
= new LoginDialog(StoreUtil
.getShell());
547 return loginDialog
.open();
551 * IMPORT/EXPORT FACTORIES
557 public static synchronized ImportHandler
getImportHandler(){
558 if(importHandler
== null){
559 importHandler
= ImportHandler
.NewInstance(getDefault().getApplicationController());
561 return importHandler
;
568 public static synchronized ExportHandler
getExportHandler(){
569 if(exportHandler
== null){
570 exportHandler
= ExportHandler
.NewInstance(getDefault().getApplicationController());
572 return exportHandler
;
576 * Whether this CdmStore is currently connected to a datasource
579 public static boolean isActive(){
580 return instance
!= null && instance
.isConnected
;
583 public static void addContextListener(IContextListener listener
){
584 contextListeners
.add(listener
);
587 public static void removeContextListener(IContextListener listener
) {
588 contextListeners
.remove(listener
);
591 public static void notifyContextStart() {
592 logger
.info("Notifying context listeners, that the context has started.");
594 instance
.authenticate();
597 for(Object listener
: contextListeners
.getListeners()){
598 ((IContextListener
) listener
).onContextEvent(EventType
.START
);
602 public static void notifyContextAboutToStop(){
603 for(Object listener
: contextListeners
.getListeners()){
604 ((IContextListener
) listener
).onContextEvent(EventType
.ABOUT_TO_STOP
);
608 public static void notifyContextStop() {
609 logger
.warn("Application event occured");
611 for(Object listener
: contextListeners
.getListeners()){
612 ((IContextListener
) listener
).onContextEvent(EventType
.STOP
);
616 public static ICdmDataSource
getDataSource(){
618 return cdmDatasource
;
623 public static void createDefaultClassification(ConversationHolder conversation
){
624 TaxonomicTree defaultClassification
= TaxonomicTree
.NewInstance("My Classification");
625 getTaxonTreeService().saveOrUpdate(defaultClassification
);
626 conversation
.commit(true);