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