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