fix #5669
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / store / CdmStoreConnector.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.taxeditor.store;
12
13 import java.lang.reflect.InvocationTargetException;
14 import java.sql.SQLException;
15 import java.util.concurrent.CancellationException;
16
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.core.runtime.Status;
20 import org.eclipse.core.runtime.jobs.Job;
21 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
22 import org.eclipse.jface.operation.IRunnableWithProgress;
23 import org.eclipse.swt.widgets.Display;
24 import org.springframework.core.io.Resource;
25
26 import eu.etaxonomy.cdm.api.application.CdmApplicationController;
27 import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteController;
28 import eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration;
29 import eu.etaxonomy.cdm.config.CdmSourceException;
30 import eu.etaxonomy.cdm.config.ICdmSource;
31 import eu.etaxonomy.cdm.database.DbSchemaValidation;
32 import eu.etaxonomy.cdm.database.ICdmDataSource;
33 import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
34 import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
35 import eu.etaxonomy.cdm.model.name.Rank;
36 import eu.etaxonomy.taxeditor.model.CdmProgressMonitorAdapter;
37 import eu.etaxonomy.taxeditor.model.MessagingUtils;
38 import eu.etaxonomy.taxeditor.remoting.source.ICdmRemoteSource;
39 import eu.etaxonomy.taxeditor.ui.dialog.LoginDialog;
40 import eu.etaxonomy.taxeditor.ui.dialog.RemotingLoginDialog;
41 import eu.etaxonomy.taxeditor.view.datasource.CdmDataSourceViewPart;
42
43
44 /**
45 * @author n.hoffmann
46 * @created Dec 8, 2010
47 * @version 1.0
48 */
49 class CdmStoreConnector extends Job {
50 private final Display display;
51 private final ICdmSource cdmSource;
52 private DbSchemaValidation dbSchemaValidation;
53 private final Resource applicationContextBean;
54
55
56 /**
57 * @param datasource
58 * @param dbSchemaValidation
59 * @param applicationContextBean
60 */
61 public CdmStoreConnector(Display display,
62 ICdmSource cdmSource,
63 DbSchemaValidation dbSchemaValidation,
64 Resource applicationContextBean) {
65 super("Connecting to datasource: " + cdmSource);
66 this.display = display;
67 this.cdmSource = cdmSource;
68 this.dbSchemaValidation = dbSchemaValidation;
69 this.applicationContextBean = applicationContextBean;
70 }
71
72
73
74 @Override
75 public IStatus run(final IProgressMonitor monitor) {
76
77 monitor.beginTask(getConnectionMessage(), 10);
78
79 // check if database is up and running
80 checkDatabaseReachable(monitor);
81
82 if (!monitor.isCanceled()) {
83 // check if the datasource actually holds data
84 checkIsNonEmptyCdmDatabase(monitor);
85 }
86
87 if (dbSchemaValidation != DbSchemaValidation.CREATE
88 && !monitor.isCanceled()) {
89 // if we do not create the datasource, we want to check if the
90 // datasource is compatible with this editor
91 checkDbSchemaVersionCompatibility(monitor);
92 }
93
94 // we are done with our low level checking and will free resources now
95 cdmSource.closeOpenConnections();
96
97 if (!monitor.isCanceled()) {
98 CdmStore.close(monitor);
99 }
100
101 ICdmApplicationConfiguration applicationController = null;
102
103 if (!monitor.isCanceled()) {
104 CdmProgressMonitorAdapter subprogressMonitor = CdmProgressMonitorAdapter
105 .CreateSubMonitor(monitor, 7);
106 // This is where we instantiate the application controller
107 int oldPriority = Thread.currentThread().getPriority();
108 try {
109 Thread.currentThread().setPriority(10);
110 applicationController = getApplicationController(cdmSource,subprogressMonitor);
111 } catch (Exception e) {
112 if(! causeIsCancelationExceptionRecursive(e)){
113 return new Status(IStatus.ERROR, "Could not connect to CDM Store", "An error occurred while trying to connect to datasource: " + cdmSource.getName(), e);
114 }
115 } finally {
116 monitor.done();
117 Thread.currentThread().setPriority(oldPriority);
118 }
119 }
120
121
122
123 if (!monitor.isCanceled()) {
124 CdmStore.setInstance(applicationController, cdmSource);
125
126 display.asyncExec(new Runnable() {
127 /*
128 * (non-Javadoc)
129 *
130 * @see java.lang.Runnable#run()
131 */
132 @Override
133 public void run() {
134 authenticate();
135
136 startContext();
137 }
138 });
139
140 MessagingUtils.info("Application context initialized.");
141 return Status.OK_STATUS;
142 } else {
143 // Show datasource view if not shown yet
144 display.asyncExec(new Runnable() {
145 /*
146 * (non-Javadoc)
147 *
148 * @see java.lang.Runnable#run()
149 */
150 @Override
151 public void run() {
152 StoreUtil.showView(CdmDataSourceViewPart.ID);
153 }
154 });
155 return Status.CANCEL_STATUS;
156 }
157
158 }
159
160 public void start(final RemotingLoginDialog loginDialog) {
161 // hide login dialog and start connection dialog
162 loginDialog.setMessage(null);
163 loginDialog.hide(true);
164
165
166 ProgressMonitorDialog dialog = new ProgressMonitorDialog(StoreUtil.getShell());
167
168 try {
169 dialog.run(true, true, new IRunnableWithProgress() {
170 @Override
171 public void run(final IProgressMonitor monitor) {
172 try {
173 monitor.beginTask(getConnectionMessage(), 7);
174
175 // check if database is up and running
176 checkDatabaseReachable(monitor);
177
178 // check if the datasource actually holds data
179 checkIsNonEmptyCdmDatabase(monitor);
180
181 if (dbSchemaValidation != DbSchemaValidation.CREATE) {
182 // if we do not create the datasource, we want to check if the
183 // datasource is compatible with this editor
184 checkDbSchemaVersionCompatibility(monitor);
185 }
186
187 // we are done with our low level checking and will free resources now
188 cdmSource.closeOpenConnections();
189
190 display.syncExec(new Runnable() {
191 /*
192 * (non-Javadoc)
193 *
194 * @see java.lang.Runnable#run()
195 */
196 @Override
197 public void run() {
198 // close the current context
199 CdmStore.close(monitor, false);
200 }
201 });
202
203 ICdmApplicationConfiguration applicationController = null;
204
205 if (!monitor.isCanceled()) {
206 CdmProgressMonitorAdapter subprogressMonitor = CdmProgressMonitorAdapter
207 .CreateSubMonitor(monitor, 3);
208 // genrerate new application controller
209 applicationController = getApplicationController(cdmSource,subprogressMonitor);
210 }
211
212
213 if (!monitor.isCanceled()) {
214 CdmStore.setInstance(applicationController, cdmSource);
215 monitor.subTask("Authenticating user");
216 display.syncExec(new Runnable() {
217 /*
218 * (non-Javadoc)
219 *
220 * @see java.lang.Runnable#run()
221 */
222 @Override
223 public void run() {
224
225 try {
226 // create new security context
227 CdmStore.getLoginManager().doAuthenticate(loginDialog.getUsername(), loginDialog.getPassword());
228 loginDialog.onComplete();
229 CdmStore.getContextManager().notifyContextStart();
230 Rank.initDefaultTerms();
231 NomenclaturalStatusType.initDefaultTerms();
232 } catch(CdmAuthenticationException cae) {
233 loginDialog.hide(false);
234 loginDialog.setMessage(cae.getMessage());
235 }
236
237 }
238 });
239 } else {
240 throw new RuntimeException("Login cancelled");
241 }
242 } finally {
243 monitor.done();
244 }
245 }
246 });
247 } catch (InvocationTargetException e) {
248 loginDialog.hide(false);
249 loginDialog.setMessage(e.getMessage());
250 } catch (InterruptedException e) {
251 loginDialog.hide(false);
252 loginDialog.setMessage(e.getMessage());
253 }
254 }
255
256
257 private ICdmApplicationConfiguration getApplicationController(ICdmSource cdmSource, CdmProgressMonitorAdapter subprogressMonitor) {
258 if(cdmSource instanceof ICdmDataSource) {
259 return CdmApplicationController.NewInstance(applicationContextBean,
260 (ICdmDataSource)cdmSource,
261 dbSchemaValidation,
262 false,
263 subprogressMonitor);
264 } else if(cdmSource instanceof ICdmRemoteSource) {
265 return CdmApplicationRemoteController.NewInstance((ICdmRemoteSource)cdmSource,
266 subprogressMonitor,
267 null);
268 } else {
269 throw new UnsupportedOperationException("Cannot create application controller for " + cdmSource.getName());
270 }
271 }
272
273 private void authenticate() {
274 LoginDialog saloginDialog = new LoginDialog(StoreUtil.getShell());
275 saloginDialog.open();
276 }
277
278 private void startContext() {
279 CdmStore.getContextManager().notifyContextStart();
280 }
281
282 /**
283 * @return
284 */
285 private String getConnectionMessage() {
286 return cdmSource.getConnectionMessage();
287 }
288
289 /**
290 * @return
291 * @throws SQLException
292 */
293 private void checkDbSchemaVersionCompatibility(IProgressMonitor monitor) {
294 monitor.subTask("Checking if datasource is compatible with this editor.");
295 String dbSchemaVersion;
296
297 String message = null;
298 try {
299 dbSchemaVersion = cdmSource.getDbSchemaVersion();
300 // we assume that empty dbSchemaVersion means an empty database and
301 // skip version checking
302
303 if(dbSchemaVersion != null) {
304 int compareVersion = CdmMetaData.compareVersion(dbSchemaVersion, CdmMetaData.getDbSchemaVersion(), 3, null);
305 // if the datasource version is greater than the taxeditor compatible version then the taxeditor needs to
306 // be updated else the datasource needs to be updated
307 if(compareVersion > 0) {
308 message = "Please update the Taxonomic Editor (Help->Check for Updates) or choose a compatible datasource";
309 } else if (compareVersion < 0) {
310 message = "Please update the chosen datasource or choose a new data source to connect to in the Datasource View.";
311 }
312 }
313 monitor.worked(1);
314 } catch (CdmSourceException e) {
315 //
316 }
317
318 if (message != null) {
319 // Show an error message
320 MessagingUtils
321 .messageDialog(
322 "Datasource Compatibility Check failed",
323 this,
324 "The database schema for the chosen "
325 + "datasource '"
326 + cdmSource
327 + "' \n is not compatible for this version of the taxonomic editor. \n\n"
328 + message,
329 null);
330
331 monitor.setCanceled(true);
332 }
333
334 }
335
336 private void checkIsNonEmptyCdmDatabase(IProgressMonitor monitor) {
337 monitor.subTask("Checking if datasource is a non empty CDM database.");
338 boolean isDbEmpty = false;
339 try {
340 isDbEmpty = cdmSource.isDbEmpty();
341 } catch (CdmSourceException e) {
342 isDbEmpty = true;
343 }
344 if(isDbEmpty) {
345 dbSchemaValidation = DbSchemaValidation.CREATE;
346 }
347 }
348
349 private boolean causeIsCancelationExceptionRecursive(Throwable throwable){
350 if(throwable == null){
351 return false;
352 }else if(throwable instanceof CancellationException){
353 return true;
354 }else{
355 return causeIsCancelationExceptionRecursive(throwable.getCause());
356 }
357 }
358
359 private void checkDatabaseReachable(IProgressMonitor monitor) {
360 try {
361 monitor.subTask("Checking if datasource is reachable.");
362 cdmSource.checkConnection();
363 monitor.worked(1);
364 } catch (CdmSourceException e) {
365 MessagingUtils.messageDialog("Could not connect to chosen datasource",
366 this, "Reason: " + e.getMessage(), e);
367 monitor.setCanceled(true);
368 }
369 }
370
371
372 }