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