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