tabbed_properties branch merged into trunk completely
[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.util.ArrayList;
14 import java.util.List;
15
16 import org.apache.log4j.Logger;
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.core.runtime.ListenerList;
19 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
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.ILocationService;
30 import eu.etaxonomy.cdm.api.service.IMediaService;
31 import eu.etaxonomy.cdm.api.service.INameService;
32 import eu.etaxonomy.cdm.api.service.IReferenceService;
33 import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
34 import eu.etaxonomy.cdm.api.service.ITaxonService;
35 import eu.etaxonomy.cdm.api.service.ITaxonTreeService;
36 import eu.etaxonomy.cdm.api.service.ITermService;
37 import eu.etaxonomy.cdm.api.service.IUserService;
38 import eu.etaxonomy.cdm.api.service.IVocabularyService;
39 import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
40 import eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator;
41 import eu.etaxonomy.cdm.database.DataSourceNotFoundException;
42 import eu.etaxonomy.cdm.database.DatabaseSchemaMismatchException;
43 import eu.etaxonomy.cdm.database.DatabaseTypeEnum;
44 import eu.etaxonomy.cdm.database.DbSchemaValidation;
45 import eu.etaxonomy.cdm.database.ICdmDataSource;
46 import eu.etaxonomy.cdm.ext.IEditGeoService;
47 import eu.etaxonomy.cdm.model.agent.AgentBase;
48 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
49 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
50 import eu.etaxonomy.cdm.model.common.Language;
51 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
52 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
53 import eu.etaxonomy.taxeditor.datasource.CdmDataSourceRepository;
54 import eu.etaxonomy.taxeditor.datasource.view.CdmDataSourceView;
55 import eu.etaxonomy.taxeditor.dialogs.LoginDialog;
56 import eu.etaxonomy.taxeditor.io.ExportHandler;
57 import eu.etaxonomy.taxeditor.io.ImportHandler;
58 import eu.etaxonomy.taxeditor.model.IContextListener;
59 import eu.etaxonomy.taxeditor.model.IContextListener.EventType;
60 import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
61
62 /**
63 * This implementation of ICdmDataRepository depends on hibernate sessions to store the data correctly
64 * for the current session. No state is held in this class.
65 *
66 * Only methods that either get or manipulate data are exposed here. So this class acts as a facade
67 * for the methods in cdmlib-service.
68 *
69 *
70 * @author n.hoffmann
71 * @created 17.03.2009
72 * @version 1.0
73 */
74 public class CdmStore{
75 private static final Logger logger = Logger.getLogger(CdmStore.class);
76
77 // FIXME change this to ClassPathResources as soon as it is included into the plugin
78 private static final Resource DEFAULT_APPLICATION_CONTEXT = new ClassPathResource("/eu/etaxonomy/cdm/editorApplicationContext.xml", TaxeditorStorePlugin.class.getClassLoader());
79 private static final DbSchemaValidation DEFAULT_DB_SCHEMA_VALIDATION = DbSchemaValidation.UPDATE;
80
81 private static CdmStore instance;
82
83 private CdmApplicationController applicationController;
84
85 private static LoginManager loginManager;
86
87 private static ImportHandler importHandler;
88
89 private static ExportHandler exportHandler;
90
91 private Language language;
92
93 private static ListenerList contextListeners = new ListenerList();
94
95 private static ICdmDataSource cdmDatasource;
96
97 private boolean isConnected;
98
99
100 /**
101 *
102 * @param applicationContextBean
103 * @return
104 */
105 protected static CdmStore getDefault(){
106 if(instance != null && instance.isConnected){
107 return instance;
108 }else if(instance == null){
109
110 initialize();
111
112 return instance;
113 }else if(!instance.isConnected){
114
115 StoreUtil.warningDialog("No datasource connection", "Application is not connected to a datastore.");
116
117 // TODO open datasource view
118
119 }
120 return null;
121 }
122
123 /**
124 * Initialize the with the last edited datasource
125 */
126 public static void initialize() {
127
128 ICdmDataSource datasource = CdmDataSourceRepository.getCurrentDataSource();
129
130 initialize(datasource);
131 }
132
133 /**
134 * Initialize with a specific datasource
135 *
136 * @param datasource
137 */
138 public static void initialize(ICdmDataSource datasource) {
139 initialize(datasource, DEFAULT_DB_SCHEMA_VALIDATION, DEFAULT_APPLICATION_CONTEXT);
140 }
141
142 /**
143 * Initialize and provide
144 *
145 * @param datasource
146 * @param dbSchemaValidation
147 * @param applicationContextBean
148 */
149 private static void initialize(final ICdmDataSource datasource, final DbSchemaValidation dbSchemaValidation, final Resource applicationContextBean){
150 if(isActive()){
151 // close();
152 logger.error("Application context already initialized.");
153 return;
154 }
155
156 logger.info("Initializing application context ...");
157
158 try {
159 if (datasource.testConnection()) {
160 cdmDatasource = datasource;
161
162 ProgressMonitorDialog progressMonitorDialog = new ProgressMonitorDialog(StoreUtil.getShell());
163 IRunnableWithProgress runnable = new IRunnableWithProgress() {
164
165 public void run(IProgressMonitor monitor) throws InvocationTargetException,
166 InterruptedException {
167 String message = "";
168 if(cdmDatasource.getDatabaseType().equals(DatabaseTypeEnum.H2)){
169 message = " local CDM Store ";
170 }else{
171 message = " CDM Community Store ";
172 }
173 message += "'" + cdmDatasource.getName() + "'";
174
175 monitor.beginTask("Establishing connection to" + message, 10);
176 // TODO get real feedback from CDMStore initialisation process
177 monitor.worked(3);
178
179 instance = new CdmStore(datasource, dbSchemaValidation, applicationContextBean);
180
181 monitor.done();
182 }
183 };
184
185 progressMonitorDialog.run(false, false, runnable);
186
187 if (!getCommonService().isDatabaseSchemaCompatible()) {
188 close();
189 // Show the datasource chooser
190 if(CdmDataSourceRepository.getAll().size() > 1){
191 // Show an error message
192 StoreUtil.errorDialog("DatabaseCompatibilityCheck failed", "The database schema for the chosen " +
193 "datasource '" + datasource + "' \n is not valid for this version of the taxonomic editor. \n" +
194 "Please choose a new data source to connect to in the Datasource View.");
195
196 // Show datasource view if not shown yet
197 StoreUtil.showView(CdmDataSourceView.ID);
198 } else {
199 throw new DatabaseSchemaMismatchException();
200 }
201 } else {
202 notifyContextStart();
203
204 logger.info("Application context initialized.");
205 }
206 }
207 } catch (DataSourceNotFoundException e) {
208 StoreUtil.errorDialog("Chosen Datasource is not available", "Could not connect to the chosen " +
209 "datasource '" + datasource + "'. Please check settings in datasources.xml. If the datasource " +
210 "is located on a remote machine, make sure you are connected to the network.");
211 logger.error(e);
212 } catch (NullPointerException e){
213 StoreUtil.errorDialog("Corrupt Datasource", "The configuration for the chosen " +
214 "datasource '" + datasource + "' is corrupt. Please check settings in datasources.xml.");
215 logger.error(e);
216 } catch (DatabaseSchemaMismatchException e){
217 StoreUtil.errorDialog("Database Schema incompatible", "The database schema for the chosen " +
218 "datasource '" + datasource + "' is not valid for this version of the taxonomic editor.");
219 logger.error(e);
220 } catch (Exception e){
221 StoreUtil.errorDialog("Unknown Datasource Error", "An error occurred while connecting to the datasource." +
222 "Please refer to the error log.");
223 logger.error(e);
224 }
225 }
226
227 /**
228 * Closes the current application context
229 */
230 public static void close(){
231 notifyContextAboutToStop();
232 if(isActive() && StoreUtil.closeAll()){
233 notifyContextStop();
234 instance.getApplicationController().close();
235 instance.isConnected = false;
236 cdmDatasource = null;
237
238 }
239 }
240
241 /**
242 *
243 */
244 private CdmStore(ICdmDataSource dataSource, DbSchemaValidation dbSchemaValidation, Resource applicationContextBean) {
245 try {
246 // this should be more modulized
247 applicationController = CdmApplicationController.NewInstance(applicationContextBean, dataSource, dbSchemaValidation, false);
248 //
249 isConnected = true;
250
251 cdmDatasource = dataSource;
252 } catch (Exception e) {
253 throw new RuntimeException(e);
254 }
255 }
256
257 /**
258 * All calls to the datastore require
259 *
260 * @return
261 */
262 private CdmApplicationController getApplicationController(){
263 try{
264 return applicationController;
265 }catch(Exception e){
266 logger.error("Exception thrown", e);
267 }
268 return null;
269 }
270
271 /*
272 * CONVERSATIONS
273 */
274
275 /**
276 * Creates a new conversation, binds resources to the conversation and
277 * start a transaction for this conversation.
278 *
279 * @return
280 */
281 public static ConversationHolder createConversation() {
282 ConversationHolder conversation = getDefault().getApplicationController().NewConversation();
283
284 conversation.startTransaction();
285 return conversation;
286 }
287
288 /*
289 * EXPOSING SERVICES
290 */
291
292 public static ITaxonService getTaxonService(){ return getDefault().getApplicationController().getTaxonService();}
293
294 public static ITaxonTreeService getTaxonTreeService() { return getDefault().getApplicationController().getTaxonTreeService();}
295
296 public static ITaxonNodeService getTaxonNodeService() { return getDefault().getApplicationController().getTaxonNodeService();}
297
298 public static INameService getNameService(){ return getDefault().getApplicationController().getNameService();}
299
300 public static IReferenceService getReferenceService(){ return getDefault().getApplicationController().getReferenceService();}
301
302 public static ILocationService getLocationService(){ return getDefault().getApplicationController().getLocationService();}
303
304 public static ProviderManager getAuthenticationManager() { return getDefault().getApplicationController().getAuthenticationManager();}
305
306 public static IUserService getUserService() { return getDefault().getApplicationController().getUserService(); }
307
308 public static ICommonService getCommonService() { return getDefault().getApplicationController().getCommonService(); }
309
310 public static IAgentService getAgentService() { return getDefault().getApplicationController().getAgentService(); }
311
312 public static ITermService getTermService() { return getDefault().getApplicationController().getTermService(); }
313
314 public static IVocabularyService getVocabularyService() { return getDefault().getApplicationController().getVocabularyService(); }
315
316 public static IMediaService getMediaService() { return getDefault().getApplicationController().getMediaService(); }
317
318 public static IEditGeoService getGeoService(){
319 return (IEditGeoService) getDefault().getApplicationController().getBean("editGeoService");
320 }
321
322 /*
323 * METHODS TO FIND ENTITIES
324 */
325
326 /**
327 *
328 * @param configurator
329 * @return
330 */
331 public static List<TaxonNameBase> findNames(IIdentifiableEntityServiceConfigurator configurator){
332 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
333 // unfortunately this is not consistently implemented in the library.
334 // FIXME use proper method once it is implemented in the library
335 String titleSearchString = configurator.getTitleSearchString().replace("*", "%");
336
337 return getNameService().getNamesByName(titleSearchString);
338 }
339
340 /**
341 *
342 * @param configurator
343 * @return
344 */
345 public static List<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator){
346 return getTaxonService().findTaxaAndNames(configurator).getRecords();
347 }
348
349 /**
350 *
351 * @param configurator
352 * @return
353 */
354 public static List<ReferenceBase> findReferences(IIdentifiableEntityServiceConfigurator configurator){
355 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
356 // unfortunately this is not consistently implemented in the library.
357 // FIXME use proper method once it is implemented in the library
358 String titleSearchString = configurator.getTitleSearchString().replace("*", "%");
359
360 return getReferenceService().findByTitle(null, titleSearchString, null, null, null, null, null, null).getRecords();
361 }
362
363 /**
364 *
365 * @param configurator
366 * @return
367 */
368 public static List<AgentBase> findAgents(IIdentifiableEntityServiceConfigurator configurator){
369 // TODO we want to use IIdentifiableEntityServiceConfigurator for all find methods
370 // unfortunately this is not consistently implemented in the library.
371 // FIXME use proper method once it is implemented in the library
372 String titleSearchString = configurator.getTitleSearchString().replace("*", "%");
373
374 return getAgentService().findByTitle(null, titleSearchString, null, null, null, null, null, null).getRecords();
375 }
376
377 public static List<TeamOrPersonBase> findTeamOrPersons(IIdentifiableEntityServiceConfigurator configurator){
378 // TODO move this to cdmlib
379 List<TeamOrPersonBase> result = new ArrayList<TeamOrPersonBase>();
380 for (AgentBase agent : findAgents(configurator)) {
381 if (agent instanceof TeamOrPersonBase) {
382 result.add((TeamOrPersonBase) agent);
383 }
384 }
385 return result;
386 }
387
388 /*
389 * LANGUAGE
390 */
391
392 /**
393 * @return
394 */
395 public static Language getDefaultLanguage(){
396 if(getDefault().getLanguage() == null){
397 getDefault().setLanguage(Language.DEFAULT());
398 }
399 return getDefault().getLanguage();
400 }
401
402 public static void setDefaultLanguage(Language language){
403 getDefault().setLanguage(language);
404 }
405
406 /**
407 * @return the language
408 */
409 private Language getLanguage() {
410 return language;
411 }
412
413 /**
414 * @param language the language to set
415 */
416 private void setLanguage(Language language) {
417 this.language = language;
418 }
419
420 /*
421 * LOGIN
422 */
423
424 /**
425 *
426 */
427 public static LoginManager getLoginManager(){
428 if(loginManager == null){
429 loginManager = new LoginManager();
430 }
431 return loginManager;
432 }
433
434 private int authenticate(){
435
436 LoginDialog loginDialog = new LoginDialog(StoreUtil.getShell());
437 return loginDialog.open();
438 }
439
440 /*
441 * IMPORT/EXPORT FACTORIES
442 */
443
444 /**
445 *
446 */
447 public static synchronized ImportHandler getImportHandler(){
448 if(importHandler == null){
449 importHandler = ImportHandler.NewInstance(getDefault().getApplicationController());
450 }
451 return importHandler;
452 }
453
454 /**
455 *
456 * @return
457 */
458 public static synchronized ExportHandler getExportHandler(){
459 if(exportHandler == null){
460 exportHandler = ExportHandler.NewInstance(getDefault().getApplicationController());
461 }
462 return exportHandler;
463 }
464
465 /**
466 * Whether this CdmStore is currently connected to a datasource
467 * @return
468 */
469 public static boolean isActive(){
470 return instance != null && instance.isConnected;
471 }
472
473 public static void addContextListener(IContextListener listener){
474 contextListeners.add(listener);
475 }
476
477 public static void removeContextListener(IContextListener listener) {
478 contextListeners.remove(listener);
479 }
480
481 public static void notifyContextStart() {
482 logger.warn("Application event occured");
483
484 instance.authenticate();
485
486
487 for(Object listener : contextListeners.getListeners()){
488 ((IContextListener) listener).onContextEvent(EventType.START);
489 }
490 }
491
492 public static void notifyContextAboutToStop(){
493 for(Object listener : contextListeners.getListeners()){
494 ((IContextListener) listener).onContextEvent(EventType.ABOUT_TO_STOP);
495 }
496 }
497
498 public static void notifyContextStop() {
499 logger.warn("Application event occured");
500
501 for(Object listener : contextListeners.getListeners()){
502 ((IContextListener) listener).onContextEvent(EventType.STOP);
503 }
504 }
505
506 public static ICdmDataSource getDataSource(){
507 if(isActive()){
508 return cdmDatasource;
509 }
510 return null;
511 }
512
513 }