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