fix #6209: adapt the message if a datasource is not available
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / model / MessagingUtils.java
1 package eu.etaxonomy.taxeditor.model;
2
3 import java.io.PrintWriter;
4 import java.io.StringWriter;
5 import java.util.ArrayList;
6 import java.util.List;
7
8 import org.apache.commons.lang.exception.ExceptionUtils;
9 import org.apache.log4j.Logger;
10 import org.eclipse.core.runtime.IStatus;
11 import org.eclipse.core.runtime.MultiStatus;
12 import org.eclipse.core.runtime.Platform;
13 import org.eclipse.core.runtime.Status;
14 import org.eclipse.jface.dialogs.MessageDialog;
15 import org.eclipse.swt.widgets.Display;
16
17 import eu.etaxonomy.cdm.test.integration.SecurityExceptionUtils;
18 import eu.etaxonomy.taxeditor.store.CdmStore;
19 import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
20
21 /**
22 * Utility class which handles all the messaging information generated by the
23 * Editor.
24 *
25 * This includes logging as well as dialogs.
26 *
27 * @author cmathew
28 *
29 */
30 public class MessagingUtils {
31 public final static String UNEXPECTED_ERROR_MESSAGE = "This is an unexpected error.";
32 public final static String CONTACT_MESSAGE = System.getProperty("line.separator") + "Please contact EDIT Support (EditSupport@bgbm.org) with the error trace below (click on the 'Details' button).";
33 public final static String DEFAULT_MESSAGE = "Error thrown but no associated message";
34 public final static String CONNECTION_FAILURE_MESSAGE = "The connection to the remote server has been broken";
35 public final static String REMOTE_ACCESS_FAILURE_MESSAGE = "Problem accessing remote server";
36 public static final String WIDGET_IS_DISPOSED_MESSAGE = "A widget was called, which was already disposed";
37
38 /**
39 * Gets the Log4J logger for a given class
40 *
41 * @param clazz
42 * a {@link java.lang.Class} object.
43 * @return a {@link org.apache.log4j.Logger} object.
44 */
45 public static Logger getLog4JLogger(Class clazz) {
46 return Logger.getLogger(clazz);
47 }
48
49 /**
50 * Logs details from a given Status object
51 *
52 * @param status
53 * a {@link org.eclipse.core.runtime.IStatus} object.
54 */
55 private static void log(IStatus status) {
56 TaxeditorStorePlugin.getDefault().getLog().log(status);
57 }
58
59 /**
60 * Logs a status object as information.
61 *
62 * @param status
63 * a {@link org.eclipse.core.runtime.IStatus} object.
64 */
65 public static void info(IStatus status) {
66 log(status);
67 }
68
69 /**
70 * Logs a string as information.
71 *
72 * @param message
73 * a {@link java.lang.String} object.
74 */
75 public static void info(String message) {
76 IStatus status = new Status(IStatus.INFO, AbstractUtility.getPluginId(), message);
77 info(status);
78 }
79
80 /**
81 * Logs an exception from a given source as a warning.
82 *
83 * @param source
84 * @param t
85 */
86 public static void warn(Class source, Throwable t) {
87 IStatus status = new Status(IStatus.WARNING, AbstractUtility.getPluginId(), t.getMessage(), t);
88 MessagingUtils.getLog4JLogger(source).warn(t);
89 log(status);
90 }
91
92 /**
93 * Logs a status object from a given source as a warning.
94 *
95 * @param source
96 * @param status
97 */
98 public static void warn(Class source, IStatus status) {
99 MessagingUtils.getLog4JLogger(source).warn(status.getMessage(), status.getException());
100 log(status);
101 }
102
103 /**
104 * Logs a string from a given source as a warning.
105 *
106 *
107 * @param source
108 * a {@link java.lang.Class} object.
109 * @param message
110 * a {@link java.lang.String} object.
111 */
112 public static void warn(Class source, String message) {
113 IStatus status = new Status(IStatus.WARNING, AbstractUtility.getPluginId(), message);
114 MessagingUtils.getLog4JLogger(source).warn(message);
115 log(status);
116 }
117
118 /**
119 * Logs a status object from a given source as an error.
120 *
121 *
122 * @param source
123 * a {@link java.lang.Class} object.
124 * @param status
125 * a {@link org.eclipse.core.runtime.IStatus} object.
126 */
127 public static void error(Class source, IStatus status) {
128 getLog4JLogger(source)
129 .error(status.getMessage(), status.getException());
130 log(status);
131 }
132
133 /**
134 * Logs a string and exception from a given source as an error.
135 *
136 *
137 * @param source
138 * a {@link java.lang.Class} object.
139 * @param message
140 * a {@link java.lang.String} object.
141 * @param t
142 * a {@link java.lang.Throwable} object.
143 */
144 public static void error(Class source, String message, Throwable t) {
145 IStatus status = new Status(IStatus.ERROR, AbstractUtility.getPluginId(), message, t);
146 error(source, status);
147 }
148
149
150
151 /**
152 * Logs an exception from a given source as an error.
153 *
154 *
155 * @param source
156 * a {@link java.lang.Class} object.
157 * @param t
158 * a {@link java.lang.Throwable} object.
159 */
160 public static void error(Class source, Throwable t) {
161 error(source.getClass(), t.getMessage(), t);
162 }
163
164
165
166 /**
167 * Returns a list of strings, providing info on,
168 * - login
169 * - editor version
170 * - server (address + source name)
171 * - db schema version
172 *
173 * @return
174 */
175 public static List<String> getContextInfo() {
176 List<String> contextInfo = new ArrayList<String>();
177 String name = "";
178 String schemaVersion = "";
179 String server = "";
180 String version = "";
181 String login = "";
182 try {
183 version = Platform.getBundle("eu.etaxonomy.taxeditor.application").getHeaders().get(org.osgi.framework.Constants.BUNDLE_VERSION);
184
185 if(CdmStore.getActiveCdmSource() != null ) {
186 login = CdmStore.getLoginManager().getAuthenticatedUser().getUsername();
187 name = CdmStore.getActiveCdmSource().getName();
188 schemaVersion = CdmStore.getActiveCdmSource().getDbSchemaVersion();
189 server = CdmStore.getActiveCdmSource().getServer();
190 }
191
192 } catch (Exception e) {
193 // Nothing to do
194 }
195 contextInfo.add("login : " + login);
196 contextInfo.add("editor version : " + version);
197 contextInfo.add("server : " + server + " / " + name);
198 contextInfo.add("schema version : " + schemaVersion);
199 contextInfo.add("os : " + System.getProperty("os.name")+" "+System.getProperty("os.version")+" "+System.getProperty("os.arch"));
200 contextInfo.add("java : "+System.getProperty("java.version"));
201
202 return contextInfo;
203 }
204
205 public static String getStackTraceAndContextInfo(Throwable t, List<String> contextInfo) {
206 StringBuffer stackTraceAndContextInfo = new StringBuffer();
207 Throwable throwable = t;
208
209 for(String infoItem : contextInfo) {
210 stackTraceAndContextInfo.append(infoItem + System.getProperty("line.separator"));
211 }
212
213 StringWriter sw = new StringWriter();
214
215 if(throwable == null) {
216 throwable = getDefaultThrowable();
217 }
218 throwable.printStackTrace(new PrintWriter(sw));
219
220 stackTraceAndContextInfo.append(sw.toString());
221
222 return stackTraceAndContextInfo.toString();
223 }
224
225 public static String getContextInfo(List<String> contextInfo) {
226 StringBuffer scontextInfoStringBuffer = new StringBuffer();
227
228
229 for(String infoItem : contextInfo) {
230 scontextInfoStringBuffer.append(infoItem + System.getProperty("line.separator"));
231 }
232
233
234
235 return scontextInfoStringBuffer.toString();
236 }
237
238 private static Throwable getDefaultThrowable() {
239 return new Throwable("Error thrown but no associated exception");
240 }
241
242
243
244 /**
245 * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
246 *
247 * @param title
248 * @param source
249 * @param t
250 * @param contextInfo
251 * @param message
252 * @param status
253 */
254 private static void errorDialog(final String title,
255 final Object source,
256 final Throwable t,
257 final List<String> contextInfo,
258 final String message,
259 final MultiStatus status,
260 final boolean showReason) {
261
262 Display.getDefault().asyncExec(new Runnable() {
263
264 @Override
265 public void run() {
266 String stackTraceWithContext = getStackTraceAndContextInfo(t, contextInfo);
267 CdmErrorDialog ced = new CdmErrorDialog(AbstractUtility.getShell(), title, message, status, stackTraceWithContext, showReason);
268 ced.open();
269 Class<? extends Object> clazz = source != null ? source.getClass() : this.getClass();
270
271
272 IStatus singleStatus = new Status(IStatus.ERROR,
273 status.getPlugin(),
274 message,
275 new Exception(stackTraceWithContext));
276
277 error(clazz, singleStatus);
278 }
279 });
280 }
281
282 public static void errorDialog(final String title,
283 final Object source,
284 final String message,
285 final String pluginId,
286 final Throwable t,
287 boolean addContactMesg) {
288 errorDialog(title, source, message, pluginId, t, addContactMesg, true);
289
290 }
291 /**
292 * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
293 *
294 * @param title
295 * @param source
296 * @param message
297 * @param pluginId
298 * @param t
299 */
300 public static void errorDialog(final String title,
301 final Object source,
302 final String message,
303 final String pluginId,
304 final Throwable t,
305 boolean addContactMesg,
306 boolean showReason) {
307
308 Throwable throwable = t;
309 StringBuffer sbStackTrace = new StringBuffer();
310
311 // We need to build a MultiStatus object since the simple
312 // idea of writing out the stack trace as a single string
313 // leads to a single line on windows
314 List<Status> childStatuses = new ArrayList<Status>();
315
316 // add context info
317 List<String> contextInfo = getContextInfo();
318 for(String infoItem : contextInfo) {
319 childStatuses.add(new Status(IStatus.ERROR, pluginId, infoItem));
320 }
321
322 if(throwable == null) {
323 throwable = getDefaultThrowable();
324 }
325
326 int thCount = 0;
327 int maxTraces = 4;
328
329 for(Throwable th : ExceptionUtils.getThrowables(throwable)) {
330 // add main exception
331 if(thCount == 0) {
332 for (StackTraceElement ste : th.getStackTrace()) {
333 childStatuses.add(new Status(IStatus.ERROR, pluginId, " at " + ste.toString()));
334 }
335 } else {
336 // add recursive causes
337 if(th != null) {
338 childStatuses.add(new Status(IStatus.ERROR, pluginId, ""));
339 String msg = th.toString();
340 childStatuses.add(new Status(IStatus.ERROR, pluginId, "Caused by : " + msg));
341 int traceCount = 0;
342 for (StackTraceElement ste : th.getStackTrace()) {
343 // add only pre-defined number of trace elements
344 if(traceCount > maxTraces) {
345 childStatuses.add(new Status(IStatus.ERROR, pluginId, " ...."));
346 break;
347 }
348 // build & add status
349 childStatuses.add(new Status(IStatus.ERROR, pluginId, " at " + ste.toString()));
350 traceCount++;
351 }
352 }
353 }
354 thCount++;
355 }
356 String finalMessage = message;
357
358 if(finalMessage == null || finalMessage.isEmpty()) {
359 finalMessage = DEFAULT_MESSAGE;
360 }
361
362 if(addContactMesg) {
363 // add edit support contact info to message
364 finalMessage += MessagingUtils.CONTACT_MESSAGE;
365 }
366
367 MultiStatus ms = new MultiStatus(pluginId,
368 IStatus.ERROR,
369 childStatuses.toArray(new Status[] {}),
370 throwable.toString(),
371 throwable);
372
373 errorDialog(title, source, throwable, contextInfo, finalMessage, ms, showReason);
374 }
375
376 /**
377 * Displays a dialog for an exception occurring in an operation.
378 *
379 * This will be either a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog} in case of a
380 * security runtime exception or a warning {@link org.eclipse.jface.dialogs.MessageDialog} in
381 * case of any other exception.
382 *
383 * @param title
384 * a {@link java.lang.String} object.
385 * @param source
386 * a {@link java.lang.Object} object.
387 * @param status
388 * a {@link org.eclipse.core.runtime.IStatus} object.
389 */
390 public static void operationDialog(final Object source,
391 final Exception ex,
392 final String pluginId,
393 final String operationlabel,
394 final String hint) {
395
396 Display.getDefault().asyncExec(new Runnable() {
397
398 @Override
399 public void run() {
400 MultiStatus info = null;
401 String title = null;
402
403 // FIXME cannot access TaxonomicEditorPlugin.PLUGIN_ID from here
404 // String PID = TaxonomicEditorPlugin.PLUGIN_ID;
405 String PID = "eu.etaxonomy.taxeditor.application";
406
407 // checking security exceptions for every operation
408 RuntimeException securityRuntimeException = SecurityExceptionUtils.findSecurityRuntimeException(ex);
409
410 // in case of a security exception it is a warning, else it is an error
411 if(securityRuntimeException != null){
412 title = "Your changes could not be saved!";
413 warningDialog(title, source, String.format("You are missing sufficient permissions for the operation \"%s\". %s", operationlabel, hint));
414 } else {
415 title = "Error executing operation";
416 errorDialog(title, source, String.format("An error occured while executing %s. %s", operationlabel, hint), pluginId, ex, true);
417
418 }
419
420
421 }
422 });
423 }
424
425
426
427
428 /**
429 * Displays a question {@link org.eclipse.jface.dialogs.MessageDialog}.
430 *
431 * @param title
432 * a {@link java.lang.String} object.
433 * @param message
434 * a {@link java.lang.String} object.
435 * @return a boolean.
436 */
437 public static boolean confirmDialog(String title, String message) {
438 return MessageDialog.openQuestion(AbstractUtility.getShell(), title, message);
439 }
440
441 /**
442 * Displays a message {@link org.eclipse.jface.dialogs.MessageDialog}.
443 *
444 * @param title
445 * @param source
446 * @param message
447 */
448 public static void messageDialog(final String title, final Object source, final String message) {
449 MessagingUtils.messageDialog(title, source, message, null, true);
450 }
451
452
453
454 /**
455 * Displays an error {@link org.eclipse.jface.dialogs.MessageDialog}.
456 *
457 * @param title
458 * The dialogs title
459 * @param source
460 * The object where the warning was generated (used by log4j)
461 * @param message
462 * An informative String to be presented to the user
463 * @param title
464 * The dialogs title
465 * @param t
466 * a Throwable if one exists or null
467 */
468 public static void messageDialog(final String title,
469 final Object source,
470 final String message,
471 final Throwable t) {
472 MessagingUtils.messageDialog(title, source, message, t, true);
473 }
474
475 /**
476 * Displays an error {@link org.eclipse.jface.dialogs.MessageDialog}.
477 *
478 * @param title
479 * The dialogs title
480 * @param source
481 * The object where the warning was generated (used by log4j)
482 * @param message
483 * An informative String to be presented to the user
484 * @param title
485 * The dialogs title
486 * @param t
487 * a Throwable if one exists or null
488 */
489 public static void messageDialog(final String title,
490 final Object source,
491 final String message,
492 final Throwable t,
493 boolean async) {
494 if(async) {
495 Display.getDefault().asyncExec(new Runnable() {
496
497 @Override
498 public void run() {
499 MessageDialog.openError(AbstractUtility.getShell(), title, message + getCauseRecursively(t));
500 Class<? extends Object> clazz = source != null ? source
501 .getClass() : this.getClass();
502 error(clazz, message, t);
503 }
504
505
506 });
507 } else {
508 MessageDialog.openError(AbstractUtility.getShell(), title, message + getCauseRecursively(t));
509 Class<? extends Object> clazz = source != null ? source.getClass() : TaxeditorStorePlugin.class;
510 error(clazz, message, t);
511 }
512 }
513
514 public static String getCauseRecursively(Throwable t) {
515 if(t == null){
516 return "";
517 }
518
519 if(t.getCause() != null){
520 return getCauseRecursively(t.getCause());
521 }else{
522 return String.format("\n\nException: %s\nMessage: %s", t.getClass().getSimpleName(), t.getMessage());
523 }
524
525 }
526 /**
527 * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
528 *
529 * @param title
530 * @param termBase
531 * @param status
532 */
533 public static void warningDialog(String title, Object source,
534 IStatus status) {
535 MessagingUtils.warningDialog(title, source, status.getMessage());
536 }
537
538 /**
539 * Standard warning dialog for the case when the application is not yet connected to the datasource
540 *
541 * @param source
542 */
543 public static void noDataSourceWarningDialog(Object source) {
544 MessagingUtils
545 .warningDialog(
546 "Application is not connected to a datastore",
547 source,
548 "The requested operation is only available when "
549 + "connected to a datasource. You may choose a datasource to connect to or create a new one in the datasource view.");
550 }
551
552 /**
553 * Standard warning dialog for the case when the datasource is not available
554 *
555 * @param source
556 */
557 public static void dataSourceNotAvailableWarningDialog(Object source) {
558 MessagingUtils
559 .warningDialog(
560 "The datasource is not available",
561 source,
562 "The choosen datasource is not available, please contact the administrator.");
563 }
564
565
566 /**
567 * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
568 *
569 * @param title
570 * The dialogs title
571 * @param source
572 * The object where the warning was generated (used by log4j)
573 * @param message
574 * An informative String to be presented to the user
575 */
576 public static void warningDialog(final String title, final Object source, final String message) {
577 Display.getDefault().asyncExec(new Runnable() {
578
579 @Override
580 public void run() {
581 MessageDialog.openWarning(AbstractUtility.getShell(), title, message);
582 Class<? extends Object> clazz = source != null ? source
583 .getClass() : AbstractUtility.class;
584 warn(clazz, message);
585 }
586 });
587 }
588
589 /**
590 * Displays an information {@link org.eclipse.jface.dialogs.MessageDialog}.
591 *
592 * @param title
593 * @param status
594 */
595 public static void informationDialog(final String title, final IStatus status) {
596 MessagingUtils.informationDialog(title, status.getMessage());
597 }
598
599 /**
600 * Displays an information {@link org.eclipse.jface.dialogs.MessageDialog}.
601 *
602 * @param title
603 * a {@link java.lang.String} object.
604 * @param message
605 * a {@link java.lang.String} object.
606 */
607 public static void informationDialog(final String title,
608 final String message) {
609 Display.getDefault().asyncExec(new Runnable() {
610
611 @Override
612 public void run() {
613 MessageDialog.openInformation(AbstractUtility.getShell(), title, message);
614 }
615 });
616 }
617
618 /**
619 * Open a message box that informs the user about unimplemented
620 * functionality. This method is for developer convenience.
621 *
622 * @param source
623 * a {@link java.lang.Object} object.
624 */
625 public static void notImplementedMessage(Object source) {
626 warningDialog("Not yet implemented", source,
627 "This functionality is not yet implemented.");
628 }
629
630 }