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