stressy commit
[taxeditor.git] / taxeditor-store / src / main / java / eu / etaxonomy / taxeditor / store / CdmStore.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9
10 package eu.etaxonomy.taxeditor.store;
11
12 import java.lang.reflect.InvocationTargetException;
13 import java.sql.SQLException;
14 import java.util.ArrayList;
15 import java.util.List;
16
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;
24
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;
69
70 /**
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.
73 *
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.
76 *
77 *
78 * @author n.hoffmann
79 * @created 17.03.2009
80 * @version 1.0
81 */
82 public class CdmStore{
83 private static final Logger logger = Logger.getLogger(CdmStore.class);
84
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;
87
88 private static CdmStore instance;
89
90 private CdmApplicationController applicationController;
91
92 private static LoginManager loginManager;
93
94 private static ImportHandler importHandler;
95
96 private static ExportHandler exportHandler;
97
98 private Language language;
99
100 private static ListenerList contextListeners = new ListenerList();
101
102 private static ICdmDataSource cdmDatasource;
103
104 private boolean isConnected;
105
106 /**
107 *
108 * @param applicationContextBean
109 * @return
110 */
111 protected static CdmStore getDefault(){
112 if(instance != null && instance.isConnected){
113 return instance;
114 }else if(instance == null){
115
116 initialize();
117
118 return instance;
119 }else if(!instance.isConnected){
120
121 StoreUtil.warningDialog("No datasource connection", "Application is not connected to a datastore.");
122
123 // TODO open datasource view
124
125 }
126 return null;
127 }
128
129 /**
130 * Initialize the with the last edited datasource
131 */
132 public static void initialize() {
133
134 ICdmDataSource datasource = CdmDataSourceRepository.getCurrentDataSource();
135
136 initialize(datasource);
137 }
138
139 /**
140 * Initialize with a specific datasource
141 *
142 * @param datasource
143 */
144 public static void initialize(ICdmDataSource datasource) {
145 initialize(datasource, DEFAULT_DB_SCHEMA_VALIDATION, DEFAULT_APPLICATION_CONTEXT);
146 }
147
148 /**
149 * Initialize and provide
150 *
151 * @param datasource
152 * @param dbSchemaValidation
153 * @param applicationContextBean
154 */
155 private static void initialize(final ICdmDataSource datasource, final DbSchemaValidation dbSchemaValidation, final Resource applicationContextBean){
156 if(isActive()){
157 // close();
158 logger.error("Application context already initialized.");
159 return;
160 }
161
162 logger.info("Initializing application context ...");
163
164 try {
165 if (datasource.testConnection() && checkDbSchemaVersionCompatibility(datasource)) {
166 cdmDatasource = datasource;
167
168 IRunnableWithProgress runnable = new IRunnableWithProgress() {
169
170 public void run(IProgressMonitor monitor) throws InvocationTargetException,
171 InterruptedException {
172 String message = "";
173 if(cdmDatasource.getDatabaseType().equals(DatabaseTypeEnum.H2)){
174 message = " local CDM Store ";
175 }else{
176 message = " CDM Community Store ";
177 }
178 message += "'" + cdmDatasource.getName() + "'";
179
180 monitor.beginTask("Establishing connection to" + message + ". \nThis might take while.", 10);
181 // TODO get real feedback from CDMStore initialisation process
182 monitor.worked(3);
183
184 instance = new CdmStore(datasource, dbSchemaValidation, applicationContextBean, new CdmProgressMonitorAdapter(monitor));
185
186 monitor.done();
187 }
188 };
189
190 StoreUtil.run(false, false, runnable);
191
192 notifyContextStart();
193 logger.info("Application context initialized.");
194
195 }
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.");
200 logger.error(e);
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.");
205 logger.error(e);
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.");
210 logger.error(e);
211 throw new RuntimeException(e);
212 }
213 }
214
215 /**
216 * @return
217 * @throws SQLException
218 */
219 private static boolean checkDbSchemaVersionCompatibility(ICdmDataSource datasource) {
220 String dbSchemaVersion;
221 try {
222 dbSchemaVersion = (String) datasource.getSingleValue(MetaDataPropertyName.DB_SCHEMA_VERSION.getSqlQuery());
223 return CdmMetaData.isDbSchemaVersionCompatible(dbSchemaVersion);
224 } catch (SQLException e) {
225
226 }
227
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.");
232
233 // Show datasource view if not shown yet
234 StoreUtil.showView(CdmDataSourceViewPart.ID);
235
236 return false;
237 }
238
239 /**
240 * Closes the current application context
241 */
242 public static void close(){
243 notifyContextAboutToStop();
244 if(isActive() && StoreUtil.closeAll()){
245 notifyContextStop();
246 instance.getApplicationController().close();
247 instance.isConnected = false;
248 cdmDatasource = null;
249
250 }
251 }
252
253 /**
254 *
255 */
256 private CdmStore(ICdmDataSource dataSource, DbSchemaValidation dbSchemaValidation, Resource applicationContextBean, eu.etaxonomy.cdm.common.IProgressMonitor progressMonitor) {
257 try {
258 // this should be more modulized
259 applicationController = CdmApplicationController.NewInstance(applicationContextBean, dataSource, dbSchemaValidation, false, progressMonitor);
260 //
261 isConnected = true;
262
263 cdmDatasource = dataSource;
264 } catch (Exception e) {
265 throw new RuntimeException(e);
266 }
267 }
268
269 /**
270 * All calls to the datastore require
271 *
272 * @return
273 */
274 private CdmApplicationController getApplicationController(){
275 try{
276 return applicationController;
277 }catch(Exception e){
278 logger.error("Exception thrown", e);
279 }
280 return null;
281 }
282
283 public static CdmApplicationController getCurrentApplicationController(){
284 return getDefault().getApplicationController();
285 }
286
287 /*
288 * CONVERSATIONS
289 */
290
291 /**
292 * Creates a new conversation, binds resources to the conversation and
293 * start a transaction for this conversation.
294 *
295 * @return
296 */
297 public static ConversationHolder createConversation() {
298 ConversationHolder conversation = getDefault().getApplicationController().NewConversation();
299
300 conversation.startTransaction();
301 return conversation;
302 }
303
304 /*
305 * EXPOSING SERVICES
306 */
307
308 public static ITaxonService getTaxonService(){ return getDefault().getApplicationController().getTaxonService();}
309
310 public static ITaxonTreeService getTaxonTreeService() { return getDefault().getApplicationController().getTaxonTreeService();}
311
312 public static ITaxonNodeService getTaxonNodeService() { return getDefault().getApplicationController().getTaxonNodeService();}
313
314 public static INameService getNameService(){ return getDefault().getApplicationController().getNameService();}
315
316 public static IReferenceService getReferenceService(){ return getDefault().getApplicationController().getReferenceService();}
317
318 public static ILocationService getLocationService(){ return getDefault().getApplicationController().getLocationService();}
319
320 public static ProviderManager getAuthenticationManager() { return getDefault().getApplicationController().getAuthenticationManager();}
321
322 public static IUserService getUserService() { return getDefault().getApplicationController().getUserService(); }
323
324 public static ICommonService getCommonService() { return getDefault().getApplicationController().getCommonService(); }
325
326 public static IAgentService getAgentService() { return getDefault().getApplicationController().getAgentService(); }
327
328 public static ITermService getTermService() { return getDefault().getApplicationController().getTermService(); }
329
330 public static IVocabularyService getVocabularyService() { return getDefault().getApplicationController().getVocabularyService(); }
331
332 public static IMediaService getMediaService() { return getDefault().getApplicationController().getMediaService(); }
333
334 public static IOccurrenceService getOccurrenceService() { return getDefault().getApplicationController().getOccurrenceService(); }
335
336 public static IFeatureTreeService getFeatureTreeService() { return getDefault().getApplicationController().getFeatureTreeService(); }
337
338 public static IFeatureNodeService getFeatureNodeService() { return getDefault().getApplicationController().getFeatureNodeService(); }
339
340 public static IEditGeoService getGeoService(){
341 return (IEditGeoService) getDefault().getApplicationController().getBean("editGeoService");
342 }
343
344 /*
345 * METHODS TO FIND ENTITIES
346 */
347
348 /**
349 *
350 * @param configurator
351 * @return
352 */
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("*", "%");
358
359 return getNameService().getNamesByName(titleSearchString);
360 }
361
362 /**
363 *
364 * @param configurator
365 * @return
366 */
367 public static List<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator){
368 return getTaxonService().findTaxaAndNames(configurator).getRecords();
369 }
370
371 /**
372 *
373 * @param configurator
374 * @return
375 */
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("*", "%");
381
382 return getReferenceService().findByTitle(null, titleSearchString, null, null, null, null, null, null).getRecords();
383 }
384
385 /**
386 *
387 * @param configurator
388 * @return
389 */
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("*", "%");
395
396 return getAgentService().findByTitle(null, titleSearchString, null, null, null, null, null, null).getRecords();
397 }
398
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);
405 }
406 }
407 return result;
408 }
409
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("*", "%");
415
416 return getOccurrenceService().findByTitle(SpecimenOrObservationBase.class, titleSearchString, null, null, null, null, null, null).getRecords();
417 }
418
419 /*
420 * LANGUAGE
421 */
422
423 /**
424 * @return
425 */
426 public static Language getDefaultLanguage(){
427 if(getDefault().getLanguage() == null){
428 getDefault().setLanguage(Language.DEFAULT());
429 }
430 return getDefault().getLanguage();
431 }
432
433 public static void setDefaultLanguage(Language language){
434 getDefault().setLanguage(language);
435 }
436
437 /**
438 * @return the language
439 */
440 private Language getLanguage() {
441 return language;
442 }
443
444 /**
445 * @param language the language to set
446 */
447 private void setLanguage(Language language) {
448 this.language = language;
449 }
450
451 /*
452 * LOGIN
453 */
454
455 /**
456 *
457 */
458 public static LoginManager getLoginManager(){
459 if(loginManager == null){
460 loginManager = new LoginManager();
461 }
462 return loginManager;
463 }
464
465 private int authenticate(){
466
467 LoginDialog loginDialog = new LoginDialog(StoreUtil.getShell());
468 return loginDialog.open();
469 }
470
471 /*
472 * IMPORT/EXPORT FACTORIES
473 */
474
475 /**
476 *
477 */
478 public static synchronized ImportHandler getImportHandler(){
479 if(importHandler == null){
480 importHandler = ImportHandler.NewInstance(getDefault().getApplicationController());
481 }
482 return importHandler;
483 }
484
485 /**
486 *
487 * @return
488 */
489 public static synchronized ExportHandler getExportHandler(){
490 if(exportHandler == null){
491 exportHandler = ExportHandler.NewInstance(getDefault().getApplicationController());
492 }
493 return exportHandler;
494 }
495
496 /**
497 * Whether this CdmStore is currently connected to a datasource
498 * @return
499 */
500 public static boolean isActive(){
501 return instance != null && instance.isConnected;
502 }
503
504 public static void addContextListener(IContextListener listener){
505 contextListeners.add(listener);
506 }
507
508 public static void removeContextListener(IContextListener listener) {
509 contextListeners.remove(listener);
510 }
511
512 public static void notifyContextStart() {
513 logger.info("Notifying context listeners, that the context has started.");
514
515 instance.authenticate();
516
517
518 for(Object listener : contextListeners.getListeners()){
519 ((IContextListener) listener).onContextEvent(EventType.START);
520 }
521 }
522
523 public static void notifyContextAboutToStop(){
524 for(Object listener : contextListeners.getListeners()){
525 ((IContextListener) listener).onContextEvent(EventType.ABOUT_TO_STOP);
526 }
527 }
528
529 public static void notifyContextStop() {
530 logger.warn("Application event occured");
531
532 for(Object listener : contextListeners.getListeners()){
533 ((IContextListener) listener).onContextEvent(EventType.STOP);
534 }
535 }
536
537 public static ICdmDataSource getDataSource(){
538 if(isActive()){
539 return cdmDatasource;
540 }
541 return null;
542 }
543
544 public static void createDefaultClassification(ConversationHolder conversation){
545 TaxonomicTree defaultClassification = TaxonomicTree.NewInstance("My Classification");
546 getTaxonTreeService().saveOrUpdate(defaultClassification);
547 conversation.commit(true);
548 }
549 }