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
.SQLException
;
14 import java
.util
.ArrayList
;
15 import java
.util
.List
;
17 import org
.apache
.log4j
.Logger
;
18 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
19 import org
.eclipse
.core
.runtime
.ListenerList
;
20 import org
.eclipse
.jface
.operation
.IRunnableWithProgress
;
21 import org
.springframework
.core
.io
.ClassPathResource
;
22 import org
.springframework
.core
.io
.Resource
;
23 import org
.springframework
.security
.authentication
.ProviderManager
;
25 import eu
.etaxonomy
.cdm
.api
.application
.CdmApplicationController
;
26 import eu
.etaxonomy
.cdm
.api
.conversation
.ConversationHolder
;
27 import eu
.etaxonomy
.cdm
.api
.service
.IAgentService
;
28 import eu
.etaxonomy
.cdm
.api
.service
.ICommonService
;
29 import eu
.etaxonomy
.cdm
.api
.service
.IFeatureNodeService
;
30 import eu
.etaxonomy
.cdm
.api
.service
.IFeatureTreeService
;
31 import eu
.etaxonomy
.cdm
.api
.service
.ILocationService
;
32 import eu
.etaxonomy
.cdm
.api
.service
.IMediaService
;
33 import eu
.etaxonomy
.cdm
.api
.service
.INameService
;
34 import eu
.etaxonomy
.cdm
.api
.service
.IOccurrenceService
;
35 import eu
.etaxonomy
.cdm
.api
.service
.IReferenceService
;
36 import eu
.etaxonomy
.cdm
.api
.service
.ITaxonNodeService
;
37 import eu
.etaxonomy
.cdm
.api
.service
.ITaxonService
;
38 import eu
.etaxonomy
.cdm
.api
.service
.ITaxonTreeService
;
39 import eu
.etaxonomy
.cdm
.api
.service
.ITermService
;
40 import eu
.etaxonomy
.cdm
.api
.service
.IUserService
;
41 import eu
.etaxonomy
.cdm
.api
.service
.IVocabularyService
;
42 import eu
.etaxonomy
.cdm
.api
.service
.config
.IIdentifiableEntityServiceConfigurator
;
43 import eu
.etaxonomy
.cdm
.api
.service
.config
.ITaxonServiceConfigurator
;
44 import eu
.etaxonomy
.cdm
.database
.DataSourceNotFoundException
;
45 import eu
.etaxonomy
.cdm
.database
.DatabaseSchemaMismatchException
;
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
.ReferenceBase
;
59 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonomicTree
;
60 import eu
.etaxonomy
.taxeditor
.datasource
.CdmDataSourceRepository
;
61 import eu
.etaxonomy
.taxeditor
.datasource
.view
.CdmDataSourceViewPart
;
62 import eu
.etaxonomy
.taxeditor
.dialogs
.LoginDialog
;
63 import eu
.etaxonomy
.taxeditor
.io
.ExportHandler
;
64 import eu
.etaxonomy
.taxeditor
.io
.ImportHandler
;
65 import eu
.etaxonomy
.taxeditor
.model
.CdmProgressMonitorAdapter
;
66 import eu
.etaxonomy
.taxeditor
.model
.IContextListener
;
67 import eu
.etaxonomy
.taxeditor
.model
.IContextListener
.EventType
;
68 import eu
.etaxonomy
.taxeditor
.store
.internal
.TaxeditorStorePlugin
;
71 * This implementation of ICdmDataRepository depends on hibernate sessions to store the data correctly
72 * for the current session. No state is held in this class.
74 * Only methods that either get or manipulate data are exposed here. So this class acts as a facade
75 * for the methods in cdmlib-service.
82 public class CdmStore
{
83 private static final Logger logger
= Logger
.getLogger(CdmStore
.class);
85 private static final Resource DEFAULT_APPLICATION_CONTEXT
= new ClassPathResource("/eu/etaxonomy/cdm/editorApplicationContext.xml", TaxeditorStorePlugin
.class.getClassLoader());
86 private static final DbSchemaValidation DEFAULT_DB_SCHEMA_VALIDATION
= DbSchemaValidation
.VALIDATE
;
88 private static CdmStore instance
;
90 private CdmApplicationController applicationController
;
92 private static LoginManager loginManager
;
94 private static ImportHandler importHandler
;
96 private static ExportHandler exportHandler
;
98 private Language language
;
100 private static ListenerList contextListeners
= new ListenerList();
102 private static ICdmDataSource cdmDatasource
;
104 private boolean isConnected
;
108 * @param applicationContextBean
111 protected static CdmStore
getDefault(){
112 if(instance
!= null && instance
.isConnected
){
114 }else if(instance
== null){
119 }else if(!instance
.isConnected
){
121 StoreUtil
.warningDialog("No datasource connection", "Application is not connected to a datastore.");
123 // TODO open datasource view
130 * Initialize the with the last edited datasource
132 public static void initialize() {
134 ICdmDataSource datasource
= CdmDataSourceRepository
.getCurrentDataSource();
136 initialize(datasource
);
140 * Initialize with a specific datasource
144 public static void initialize(ICdmDataSource datasource
) {
145 initialize(datasource
, DEFAULT_DB_SCHEMA_VALIDATION
, DEFAULT_APPLICATION_CONTEXT
);
149 * Initialize and provide
152 * @param dbSchemaValidation
153 * @param applicationContextBean
155 private static void initialize(final ICdmDataSource datasource
, final DbSchemaValidation dbSchemaValidation
, final Resource applicationContextBean
){
158 logger
.error("Application context already initialized.");
162 logger
.info("Initializing application context ...");
165 if (datasource
.testConnection() && checkDbSchemaVersionCompatibility(datasource
)) {
166 cdmDatasource
= datasource
;
168 IRunnableWithProgress runnable
= new IRunnableWithProgress() {
170 public void run(IProgressMonitor monitor
) throws InvocationTargetException
,
171 InterruptedException
{
173 if(cdmDatasource
.getDatabaseType().equals(DatabaseTypeEnum
.H2
)){
174 message
= " local CDM Store ";
176 message
= " CDM Community Store ";
178 message
+= "'" + cdmDatasource
.getName() + "'";
180 monitor
.beginTask("Establishing connection to" + message
+ ". \nThis might take while.", 10);
181 // TODO get real feedback from CDMStore initialisation process
184 instance
= new CdmStore(datasource
, dbSchemaValidation
, applicationContextBean
, new CdmProgressMonitorAdapter(monitor
));
190 StoreUtil
.run(false, false, runnable
);
192 notifyContextStart();
193 logger
.info("Application context initialized.");
196 } catch (DataSourceNotFoundException e
) {
197 StoreUtil
.errorDialog("Chosen Datasource is not available", "Could not connect to the chosen " +
198 "datasource '" + datasource
+ "'. Please check settings in datasources.xml. If the datasource " +
199 "is located on a remote machine, make sure you are connected to the network.");
201 throw new RuntimeException(e
);
202 } catch (NullPointerException e
){
203 StoreUtil
.errorDialog("NullPointerException", "A NullPointerException occured while connecting to this " +
204 "datasource '" + datasource
+ "'. Please check refer to the error log.");
206 throw new RuntimeException(e
);
207 } catch (Exception e
){
208 StoreUtil
.errorDialog("Unknown Datasource Error", "An error occurred while connecting to the datasource." +
209 "Please refer to the error log.");
211 throw new RuntimeException(e
);
217 * @throws SQLException
219 private static boolean checkDbSchemaVersionCompatibility(ICdmDataSource datasource
) {
220 String dbSchemaVersion
;
222 dbSchemaVersion
= (String
) datasource
.getSingleValue(MetaDataPropertyName
.DB_SCHEMA_VERSION
.getSqlQuery());
223 return CdmMetaData
.isDbSchemaVersionCompatible(dbSchemaVersion
);
224 } catch (SQLException e
) {
228 // Show an error message
229 StoreUtil
.errorDialog("DatabaseCompatibilityCheck failed", "The database schema for the chosen " +
230 "datasource '" + datasource
+ "' \n is not valid for this version of the taxonomic editor. \n" +
231 "Please choose a new data source to connect to in the Datasource View.");
233 // Show datasource view if not shown yet
234 StoreUtil
.showView(CdmDataSourceViewPart
.ID
);
240 * Closes the current application context
242 public static void close(){
243 notifyContextAboutToStop();
244 if(isActive() && StoreUtil
.closeAll()){
246 instance
.getApplicationController().close();
247 instance
.isConnected
= false;
248 cdmDatasource
= null;
256 private CdmStore(ICdmDataSource dataSource
, DbSchemaValidation dbSchemaValidation
, Resource applicationContextBean
, eu
.etaxonomy
.cdm
.common
.IProgressMonitor progressMonitor
) {
258 // this should be more modulized
259 applicationController
= CdmApplicationController
.NewInstance(applicationContextBean
, dataSource
, dbSchemaValidation
, false, progressMonitor
);
263 cdmDatasource
= dataSource
;
264 } catch (Exception e
) {
265 throw new RuntimeException(e
);
270 * All calls to the datastore require
274 private CdmApplicationController
getApplicationController(){
276 return applicationController
;
278 logger
.error("Exception thrown", e
);
283 public static CdmApplicationController
getCurrentApplicationController(){
284 return getDefault().getApplicationController();
292 * Creates a new conversation, binds resources to the conversation and
293 * start a transaction for this conversation.
297 public static ConversationHolder
createConversation() {
298 ConversationHolder conversation
= getDefault().getApplicationController().NewConversation();
300 conversation
.startTransaction();
308 public static ITaxonService
getTaxonService(){ return getDefault().getApplicationController().getTaxonService();}
310 public static ITaxonTreeService
getTaxonTreeService() { return getDefault().getApplicationController().getTaxonTreeService();}
312 public static ITaxonNodeService
getTaxonNodeService() { return getDefault().getApplicationController().getTaxonNodeService();}
314 public static INameService
getNameService(){ return getDefault().getApplicationController().getNameService();}
316 public static IReferenceService
getReferenceService(){ return getDefault().getApplicationController().getReferenceService();}
318 public static ILocationService
getLocationService(){ return getDefault().getApplicationController().getLocationService();}
320 public static ProviderManager
getAuthenticationManager() { return getDefault().getApplicationController().getAuthenticationManager();}
322 public static IUserService
getUserService() { return getDefault().getApplicationController().getUserService(); }
324 public static ICommonService
getCommonService() { return getDefault().getApplicationController().getCommonService(); }
326 public static IAgentService
getAgentService() { return getDefault().getApplicationController().getAgentService(); }
328 public static ITermService
getTermService() { return getDefault().getApplicationController().getTermService(); }
330 public static IVocabularyService
getVocabularyService() { return getDefault().getApplicationController().getVocabularyService(); }
332 public static IMediaService
getMediaService() { return getDefault().getApplicationController().getMediaService(); }
334 public static IOccurrenceService
getOccurrenceService() { return getDefault().getApplicationController().getOccurrenceService(); }
336 public static IFeatureTreeService
getFeatureTreeService() { return getDefault().getApplicationController().getFeatureTreeService(); }
338 public static IFeatureNodeService
getFeatureNodeService() { return getDefault().getApplicationController().getFeatureNodeService(); }
340 public static IEditGeoService
getGeoService(){
341 return (IEditGeoService
) getDefault().getApplicationController().getBean("editGeoService");
345 * METHODS TO FIND ENTITIES
350 * @param configurator
353 public static List
<TaxonNameBase
> findNames(IIdentifiableEntityServiceConfigurator configurator
){
354 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
355 // unfortunately this is not consistently implemented in the library.
356 // FIXME use proper method once it is implemented in the library
357 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
359 return getNameService().getNamesByName(titleSearchString
);
364 * @param configurator
367 public static List
<IdentifiableEntity
> findTaxaAndNames(ITaxonServiceConfigurator configurator
){
368 return getTaxonService().findTaxaAndNames(configurator
).getRecords();
373 * @param configurator
376 public static List
<ReferenceBase
> findReferences(IIdentifiableEntityServiceConfigurator configurator
){
377 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
378 // unfortunately this is not consistently implemented in the library.
379 // FIXME use proper method once it is implemented in the library
380 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
382 return getReferenceService().findByTitle(null, titleSearchString
, null, null, null, null, null, null).getRecords();
387 * @param configurator
390 public static List
<AgentBase
> findAgents(IIdentifiableEntityServiceConfigurator configurator
){
391 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
392 // unfortunately this is not consistently implemented in the library.
393 // FIXME use proper method once it is implemented in the library
394 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
396 return getAgentService().findByTitle(null, titleSearchString
, null, null, null, null, null, null).getRecords();
399 public static List
<TeamOrPersonBase
> findTeamOrPersons(IIdentifiableEntityServiceConfigurator configurator
){
400 // TODO move this to cdmlib
401 List
<TeamOrPersonBase
> result
= new ArrayList
<TeamOrPersonBase
>();
402 for (AgentBase agent
: findAgents(configurator
)) {
403 if (agent
instanceof TeamOrPersonBase
) {
404 result
.add((TeamOrPersonBase
) agent
);
410 public static List
<SpecimenOrObservationBase
> findOccurrences(IIdentifiableEntityServiceConfigurator configurator
){
411 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
412 // unfortunately this is not consistently implemented in the library.
413 // FIXME use proper method once it is implemented in the library
414 String titleSearchString
= configurator
.getTitleSearchString().replace("*", "%");
416 return getOccurrenceService().findByTitle(SpecimenOrObservationBase
.class, titleSearchString
, null, null, null, null, null, null).getRecords();
426 public static Language
getDefaultLanguage(){
427 if(getDefault().getLanguage() == null){
428 getDefault().setLanguage(Language
.DEFAULT());
430 return getDefault().getLanguage();
433 public static void setDefaultLanguage(Language language
){
434 getDefault().setLanguage(language
);
438 * @return the language
440 private Language
getLanguage() {
445 * @param language the language to set
447 private void setLanguage(Language language
) {
448 this.language
= language
;
458 public static LoginManager
getLoginManager(){
459 if(loginManager
== null){
460 loginManager
= new LoginManager();
465 private int authenticate(){
467 LoginDialog loginDialog
= new LoginDialog(StoreUtil
.getShell());
468 return loginDialog
.open();
472 * IMPORT/EXPORT FACTORIES
478 public static synchronized ImportHandler
getImportHandler(){
479 if(importHandler
== null){
480 importHandler
= ImportHandler
.NewInstance(getDefault().getApplicationController());
482 return importHandler
;
489 public static synchronized ExportHandler
getExportHandler(){
490 if(exportHandler
== null){
491 exportHandler
= ExportHandler
.NewInstance(getDefault().getApplicationController());
493 return exportHandler
;
497 * Whether this CdmStore is currently connected to a datasource
500 public static boolean isActive(){
501 return instance
!= null && instance
.isConnected
;
504 public static void addContextListener(IContextListener listener
){
505 contextListeners
.add(listener
);
508 public static void removeContextListener(IContextListener listener
) {
509 contextListeners
.remove(listener
);
512 public static void notifyContextStart() {
513 logger
.info("Notifying context listeners, that the context has started.");
515 instance
.authenticate();
518 for(Object listener
: contextListeners
.getListeners()){
519 ((IContextListener
) listener
).onContextEvent(EventType
.START
);
523 public static void notifyContextAboutToStop(){
524 for(Object listener
: contextListeners
.getListeners()){
525 ((IContextListener
) listener
).onContextEvent(EventType
.ABOUT_TO_STOP
);
529 public static void notifyContextStop() {
530 logger
.warn("Application event occured");
532 for(Object listener
: contextListeners
.getListeners()){
533 ((IContextListener
) listener
).onContextEvent(EventType
.STOP
);
537 public static ICdmDataSource
getDataSource(){
539 return cdmDatasource
;
544 public static void createDefaultClassification(ConversationHolder conversation
){
545 TaxonomicTree defaultClassification
= TaxonomicTree
.NewInstance("My Classification");
546 getTaxonTreeService().saveOrUpdate(defaultClassification
);
547 conversation
.commit(true);