#5029 Add functionality for login to remember credentials, reconnect and switch user
[taxeditor.git] / eu.etaxonomy.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.util.EnumSet;
13
14 import org.eclipse.core.runtime.IProgressMonitor;
15 import org.eclipse.core.runtime.jobs.Job;
16 import org.eclipse.swt.widgets.Display;
17 import org.springframework.core.io.ClassPathResource;
18 import org.springframework.core.io.Resource;
19 import org.springframework.security.access.PermissionEvaluator;
20 import org.springframework.security.authentication.AuthenticationManager;
21 import org.springframework.security.core.Authentication;
22 import org.springframework.security.core.context.SecurityContext;
23 import org.springframework.security.core.context.SecurityContextHolder;
24
25 import eu.etaxonomy.cdm.api.application.CdmApplicationException;
26 import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteController;
27 import eu.etaxonomy.cdm.api.application.CdmApplicationState;
28 import eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration;
29 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
30 import eu.etaxonomy.cdm.api.service.ICommonService;
31 import eu.etaxonomy.cdm.api.service.IService;
32 import eu.etaxonomy.cdm.config.ICdmSource;
33 import eu.etaxonomy.cdm.database.DbSchemaValidation;
34 import eu.etaxonomy.cdm.ext.geo.IEditGeoService;
35 import eu.etaxonomy.cdm.model.common.CdmBase;
36 import eu.etaxonomy.cdm.model.common.Language;
37 import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
38 import eu.etaxonomy.cdm.persistence.hibernate.permission.ICdmPermissionEvaluator;
39 import eu.etaxonomy.cdm.persistence.hibernate.permission.Role;
40 import eu.etaxonomy.taxeditor.datasource.CdmDataSourceRepository;
41 import eu.etaxonomy.taxeditor.io.ExportManager;
42 import eu.etaxonomy.taxeditor.io.ImportManager;
43 import eu.etaxonomy.taxeditor.model.AbstractUtility;
44 import eu.etaxonomy.taxeditor.model.MessagingUtils;
45 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
46 import eu.etaxonomy.taxeditor.remoting.cache.CdmRemoteCacheManager;
47 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager;
48 import eu.etaxonomy.taxeditor.session.mock.MockCdmEntitySessionManager;
49 import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
50 import eu.etaxonomy.taxeditor.ui.dialog.RemotingLoginDialog;
51 import eu.etaxonomy.taxeditor.view.datasource.CdmDataSourceViewPart;
52
53 /**
54 * This implementation of ICdmDataRepository depends on hibernate sessions to
55 * store the data correctly for the current session. No state is held in this
56 * class.
57 *
58 * Only methods that either get or manipulate data are exposed here. So this
59 * class acts as a facade for the methods in cdmlib-service.
60 *
61 * @author n.hoffmann
62 * @created 17.03.2009
63 * @version 1.0
64 */
65 public class CdmStore {
66
67 private static final Resource DEFAULT_APPLICATION_CONTEXT = new ClassPathResource(
68 "/eu/etaxonomy/cdm/editorApplicationContext.xml",
69 TaxeditorStorePlugin.class);
70 private static final DbSchemaValidation DEFAULT_DB_SCHEMA_VALIDATION = DbSchemaValidation.VALIDATE;
71
72 private static CdmStore instance;
73
74 //private final ICdmApplicationConfiguration applicationConfiguration;
75
76 private static ContextManager contextManager = new ContextManager();
77
78 private static LoginManager loginManager = new LoginManager();
79
80 private static TermManager termManager = new TermManager();
81
82 private static SearchManager searchManager = new SearchManager();
83
84 private static EditorManager editorManager = new EditorManager();
85
86 private static UseObjectStore useObjectInitializer = new UseObjectStore();
87
88 private static CdmStoreConnector job;
89
90 private Language language;
91
92 private ICdmSource cdmSource;
93
94 private boolean isConnected;
95
96
97
98 /**
99 * <p>
100 * getDefault
101 * </p>
102 *
103 * @return a {@link eu.etaxonomy.taxeditor.store.CdmStore} object.
104 */
105 protected static CdmStore getDefault() {
106 if (instance != null && instance.isConnected) {
107 return instance;
108 } else{// if (instance == null || !instance.isConnected) {
109
110 MessagingUtils.noDataSourceWarningDialog(instance);
111
112 AbstractUtility.showView(CdmDataSourceViewPart.ID);
113 return null;
114 }
115 }
116
117 /**
118 * Initialize the with the last edited datasource
119 */
120 public static void connect() {
121
122 ICdmSource cdmSource;
123 try {
124
125 cdmSource = CdmDataSourceRepository.getCurrentCdmSource();
126 connect(cdmSource);
127 } catch (Exception e) {
128 MessagingUtils.messageDialog("Connection to CDM Source Failed", CdmStore.class, "Could not connect to target CDM Source", e);
129 }
130
131
132 }
133
134 /**
135 * Initialize with a specific datasource
136 *
137 * @param datasource
138 * a {@link eu.etaxonomy.cdm.database.ICdmDataSource} object.
139 */
140 public static void connect(ICdmSource cdmSource) {
141 connect(cdmSource, DEFAULT_DB_SCHEMA_VALIDATION,
142 DEFAULT_APPLICATION_CONTEXT);
143 }
144
145 public static void connect(ICdmSource cdmSource, RemotingLoginDialog loginDialog) {
146 connect(cdmSource,
147 DEFAULT_DB_SCHEMA_VALIDATION,
148 DEFAULT_APPLICATION_CONTEXT,
149 loginDialog);
150 }
151
152 /**
153 * Initialize and provide
154 *
155 * @param datasource
156 * @param dbSchemaValidation
157 * @param applicationContextBean
158 */
159 private static void connect(final ICdmSource cdmSource,
160 final DbSchemaValidation dbSchemaValidation,
161 final Resource applicationContextBean) {
162
163 MessagingUtils.info("Connecting to datasource: " + cdmSource);
164
165 job = new CdmStoreConnector(Display.getDefault(), cdmSource,
166 dbSchemaValidation, applicationContextBean);
167 job.setUser(true);
168 job.setPriority(Job.BUILD);
169 job.schedule();
170
171 }
172
173 private static void connect(final ICdmSource cdmSource,
174 final DbSchemaValidation dbSchemaValidation,
175 final Resource applicationContextBean,
176 RemotingLoginDialog remotingLoginDialog) {
177 RemotingLoginDialog loginDialog = remotingLoginDialog;
178
179 MessagingUtils.info("Connecting to datasource: " + cdmSource);
180
181 job = new CdmStoreConnector(Display.getDefault(),
182 cdmSource,
183 dbSchemaValidation,
184 applicationContextBean);
185 job.start(loginDialog);
186 if(isActive()) {
187 // before we connect we clear the entity caches and the sessions
188 CdmRemoteCacheManager.removeEntityCaches();
189 if(getCurrentSessionManager() != null) {
190 getCurrentSessionManager().disposeAll();
191 }
192 }
193 }
194
195 public static boolean isConnecting() {
196 return job != null && job.getState() == Job.RUNNING;
197 }
198
199 /**
200 * Closes the current application context
201 *
202 * @param monitor
203 * a {@link org.eclipse.core.runtime.IProgressMonitor} object.
204 */
205 public static void close(final IProgressMonitor monitor) {
206 Display.getDefault().asyncExec(new Runnable() {
207 /*
208 * (non-Javadoc)
209 *
210 * @see java.lang.Runnable#run()
211 */
212 @Override
213 public void run() {
214 getContextManager().notifyContextAboutToStop(monitor);
215 if ((monitor == null || (!monitor.isCanceled()) && isActive())) {
216 getContextManager().notifyContextStop(monitor);
217 instance.close();
218 }
219 }
220 });
221 }
222
223 public static void close(IProgressMonitor monitor, boolean async) {
224 if(async) {
225 close(monitor);
226 } else {
227 getContextManager().notifyContextAboutToStop(monitor);
228 if ((monitor == null || (!monitor.isCanceled()) && isActive())) {
229 getContextManager().notifyContextStop(monitor);
230 instance.close();
231 }
232 }
233
234 }
235 private void close() {
236 isConnected = false;
237 cdmSource = null;
238 CdmApplicationState.dispose();
239 }
240
241 static void setInstance(ICdmApplicationConfiguration applicationController,
242 ICdmSource cdmSource) {
243 instance = new CdmStore(applicationController, cdmSource);
244 }
245
246 private CdmStore(ICdmApplicationConfiguration applicationController,
247 ICdmSource cdmSource) {
248 CdmApplicationState.setCurrentAppConfig(applicationController);
249 CdmApplicationState.setCurrentDataChangeService(new CdmUIDataChangeService());
250 this.cdmSource = cdmSource;
251 isConnected = true;
252 }
253
254 /**
255 * All calls to the datastore require
256 *
257 * @return
258 */
259 private ICdmApplicationConfiguration getApplicationConfiguration() {
260 try {
261 return CdmApplicationState.getCurrentAppConfig();
262 } catch (Exception e) {
263 MessagingUtils.error(CdmStore.class, e);
264 }
265 return null;
266 }
267
268 /**
269 * <p>
270 * getCurrentApplicationController
271 * </p>
272 *
273 * @return a
274 * {@link eu.etaxonomy.cdm.remote.api.application.CdmApplicationController}
275 * object.
276 */
277 public static ICdmApplicationConfiguration getCurrentApplicationConfiguration() {
278 if (getDefault() != null) {
279 return getDefault().getApplicationConfiguration();
280 }
281 return null;
282 }
283
284 /*
285 * CONVERSATIONS
286 */
287
288 /**
289 * Creates a new conversation, binds resources to the conversation and start
290 * a transaction for this conversation.
291 *
292 * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
293 * object.
294 */
295 public static ConversationHolder createConversation() {
296 ConversationHolder conversation = getCurrentApplicationConfiguration()
297 .NewConversation();
298 try {
299 conversation.startTransaction();
300 }catch(Exception e){
301 MessagingUtils.messageDialog("No database connection", CdmStore.class, "No database connection available", e);
302 }
303 return conversation;
304 }
305
306 //FIXME:Remoting should be removed after moving completely to remoting
307 private MockCdmEntitySessionManager mockCdmEntitySessionManager;
308
309 private ICdmEntitySessionManager getSessionManager() {
310 //FIXME:Remoting we should only have CdmApplicationRemoteConfiguration after move to remoting
311 // bad hack which should be finally removed
312 if(getCurrentApplicationConfiguration() instanceof CdmApplicationRemoteController) {
313 return ((CdmApplicationRemoteController)getCurrentApplicationConfiguration()).getCdmEntitySessionManager();
314 } else {
315 if(mockCdmEntitySessionManager == null) {
316 mockCdmEntitySessionManager = new MockCdmEntitySessionManager();
317 }
318 return mockCdmEntitySessionManager;
319 }
320 }
321
322 public static ICdmEntitySessionManager getCurrentSessionManager() {
323 if (getDefault() != null) {
324 return getDefault().getSessionManager();
325 }
326 return null;
327
328 }
329
330 /**
331 * Generic method that will scan the getters of {@link ICdmApplicationConfiguration} for the given service
332 * interface. If a matching getter is found the according service implementation is returned by
333 * invoking the getter otherwise the method returns <code>null</code>.
334 *
335 * @param <T>
336 * @param serviceClass
337 * @return the configured implementation of <code>serviceClass</code> or <code>null</code>
338 */
339 public static <T extends IService> T getService(Class<T> serviceClass) {
340 T service = null;
341 try {
342 service = CdmApplicationState.getService(serviceClass);
343 } catch (CdmApplicationException cae) {
344 MessagingUtils.error(CdmStore.class, cae);
345 }
346
347 return service;
348 }
349
350 /**
351 * @see #getService(Class)
352 * As ICommonService is not extending IService we need a specific request here
353 */
354 public static ICommonService getCommonService() {
355 return CdmApplicationState.getCommonService();
356
357 }
358
359 /**
360 * <p>
361 * getAuthenticationManager
362 * </p>
363 *
364 * @return a
365 * {@link org.springframework.security.authentication.ProviderManager}
366 * object.
367 */
368 public static AuthenticationManager getAuthenticationManager() {
369 return getCurrentApplicationConfiguration().getAuthenticationManager();
370 }
371
372 /**
373 * <p>
374 * getAuthenticationManager
375 * </p>
376 *
377 * @return a
378 * {@link ICdmPermissionEvaluator} object.
379 */
380 public static ICdmPermissionEvaluator getPermissionEvaluator() {
381 return getCurrentApplicationConfiguration().getPermissionEvaluator();
382 }
383
384 /**
385 * <p>
386 * getGeoService
387 * </p>
388 *
389 * @return a {@link eu.etaxonomy.cdm.ext.geo.IEditGeoService} object.
390 */
391 public static IEditGeoService getGeoService() {
392 return (IEditGeoService) getCurrentApplicationConfiguration().getBean(
393 "editGeoService");
394 }
395
396 /*
397 * SECURITY RELATED CONVENIENCE METHODS
398 */
399
400 /**
401 * @see org.springframework.security.access.PermissionEvaluator#hasPermission(org.springframework.security.core.Authentication, java.lang.Object, java.lang.Object)
402 *
403 * @param targetDomainObject
404 * @param permission
405 * @return
406 */
407 public static boolean currentAuthentiationHasPermission(CdmBase targetDomainObject, EnumSet<CRUD> permission){
408 //TODO use getCurrentApplicationConfiguration().currentAuthentiationHasPermission(CdmBase targetDomainObject, Operation permission) instead
409 SecurityContext context = SecurityContextHolder.getContext();
410 PermissionEvaluator pe = getPermissionEvaluator();
411 boolean hasPermission = false;
412 try {
413 hasPermission = getPermissionEvaluator().hasPermission(context.getAuthentication(), targetDomainObject,
414 permission);
415 } catch (org.springframework.security.access.AccessDeniedException e) {
416 /* IGNORE */
417 }
418 return hasPermission;
419 }
420
421 /**
422 * @see org.springframework.security.access.PermissionEvaluator#hasPermission(org.springframework.security.core.Authentication, java.lang.Object, java.lang.Object)
423 *
424 * @param targetDomainObject
425 * @param permission
426 * @return
427 */
428 public static boolean currentAuthentiationHasPermission(Class<? extends CdmBase> targetType, EnumSet<CRUD> permission){
429 boolean hasPermission = false;
430 try {
431 hasPermission = getPermissionEvaluator().hasPermission(getCurrentAuthentiation(), null, targetType.getName(), permission);
432 } catch (org.springframework.security.access.AccessDeniedException e) {
433 /* IGNORE */
434 }
435 return hasPermission;
436 }
437
438 public static boolean currentAuthentiationHasOneOfRoles(Role ... roles){
439 boolean hasPermission = false;
440 try {
441 hasPermission = getPermissionEvaluator().hasOneOfRoles(getCurrentAuthentiation(), roles);
442 } catch (org.springframework.security.access.AccessDeniedException e) {
443 /* IGNORE */
444 }
445 return hasPermission;
446 }
447
448 public static Authentication getCurrentAuthentiation() {
449 SecurityContext context = SecurityContextHolder.getContext();
450 return context.getAuthentication();
451 }
452
453 /*
454 * LANGUAGE
455 */
456
457 /**
458 * Provides access to the global default language set in the application preferences.
459 *
460 * @return a {@link eu.etaxonomy.cdm.model.common.Language} object.
461 */
462 public static Language getDefaultLanguage() {
463 if (getDefault().getLanguage() == null) {
464 getDefault().setLanguage(PreferencesUtil.getGlobalLanguage());
465 }
466 return getDefault().getLanguage();
467 }
468
469 /**
470 * <p>
471 * setDefaultLanguage
472 * </p>
473 *
474 * @param language
475 * a {@link eu.etaxonomy.cdm.model.common.Language} object.
476 */
477 public static void setDefaultLanguage(Language language) {
478 getDefault().setLanguage(language);
479 }
480
481 /**
482 * @return the language
483 */
484 private Language getLanguage() {
485 return language;
486 }
487
488 /**
489 * @param language
490 * the language to set
491 */
492 private void setLanguage(Language language) {
493 this.language = language;
494 }
495
496 /*
497 * LOGIN
498 */
499
500 /**
501 * <p>
502 * Getter for the field <code>loginManager</code>.
503 * </p>
504 *
505 * @return a {@link eu.etaxonomy.taxeditor.store.LoginManager} object.
506 */
507 public static LoginManager getLoginManager() {
508 return loginManager;
509 }
510
511 /**
512 * <p>
513 * Getter for the field <code>contextManager</code>.
514 * </p>
515 *
516 * @return a {@link eu.etaxonomy.taxeditor.store.ContextManager} object.
517 */
518 public static ContextManager getContextManager() {
519 return contextManager;
520 }
521
522 public static TermManager getTermManager() {
523 return termManager;
524 }
525
526 public static SearchManager getSearchManager() {
527 return searchManager;
528 }
529
530 public static EditorManager getEditorManager() {
531 return editorManager;
532 }
533
534 /*
535 * IMPORT/EXPORT FACTORIES
536 */
537
538 /**
539 * <p>
540 * Getter for the field <code>importHandler</code>.
541 * </p>
542 *
543 * @return a {@link eu.etaxonomy.taxeditor.io.ImportManager} object.
544 */
545 public static ImportManager getImportManager() {
546 return ImportManager.NewInstance(getCurrentApplicationConfiguration());
547 }
548
549 /**
550 * <p>
551 * Getter for the field <code>exportHandler</code>.
552 * </p>
553 *
554 * @return a {@link eu.etaxonomy.taxeditor.io.ExportManager} object.
555 */
556 public static ExportManager getExportManager() {
557 return ExportManager.NewInstance(getCurrentApplicationConfiguration());
558 }
559
560 /**
561 * Whether this CdmStore is currently connected to a datasource
562 *
563 * @return a boolean.
564 */
565 public static boolean isActive() {
566 return instance != null && instance.isConnected;
567 }
568
569 public static ICdmSource getActiveCdmSource() {
570 if (isActive()) {
571 return instance.getCdmSource();
572 }
573 return null;
574 }
575
576 /**
577 * <p>
578 * getDataSource
579 * </p>
580 *
581 * @return a {@link eu.etaxonomy.cdm.database.ICdmDataSource} object.
582 * @deprecated currently retained for backward compatibility - use {@link getActiveCdmSource()} instead
583 */
584 // public static ICdmDataSource getDataSource() {
585 // if (isActive()) {
586 // return (ICdmDataSource)instance.getCdmSource();
587 // }
588 // return null;
589 // }
590
591 /**
592 * @return
593 */
594 private ICdmSource getCdmSource() {
595 return cdmSource;
596 }
597
598 }