Project

General

Profile

Download (19 KB) Statistics
| Branch: | Tag: | Revision:
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

    
197
        return contextInfo;
198
    }
199

    
200
    public static String getStackTraceAndContextInfo(Throwable t, List<String> contextInfo)  {
201
        StringBuffer stackTraceAndContextInfo = new StringBuffer();
202
        Throwable throwable = t;
203

    
204
        for(String infoItem : contextInfo) {
205
            stackTraceAndContextInfo.append(infoItem + System.getProperty("line.separator"));
206
        }
207

    
208
        StringWriter sw = new StringWriter();
209

    
210
        if(throwable == null) {
211
            throwable = getDefaultThrowable();
212
        }
213
        throwable.printStackTrace(new PrintWriter(sw));
214

    
215
        stackTraceAndContextInfo.append(sw.toString());
216

    
217
        return stackTraceAndContextInfo.toString();
218
    }
219

    
220
    private static Throwable getDefaultThrowable() {
221
        return new Throwable("Error thrown but no associated exception");
222
    }
223

    
224

    
225

    
226
    /**
227
     * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
228
     *
229
     * @param title
230
     * @param source
231
     * @param t
232
     * @param contextInfo
233
     * @param message
234
     * @param status
235
     */
236
    private static void errorDialog(final String title,
237
            final Object source,
238
            final Throwable t,
239
            final List<String> contextInfo,
240
            final String message,
241
            final MultiStatus status) {
242

    
243
        Display.getDefault().asyncExec(new Runnable() {
244

    
245
            @Override
246
            public void run() {
247
                String stackTraceWithContext = getStackTraceAndContextInfo(t, contextInfo);
248
                CdmErrorDialog ced = new CdmErrorDialog(AbstractUtility.getShell(), title, message, status, stackTraceWithContext);
249
                ced.open();
250
                Class<? extends Object> clazz = source != null ? source.getClass() : this.getClass();
251

    
252

    
253
                IStatus singleStatus = new Status(IStatus.ERROR,
254
                        status.getPlugin(),
255
                        message,
256
                        new Exception(stackTraceWithContext));
257

    
258
                error(clazz, singleStatus);
259
            }
260
        });
261
    }
262

    
263
    /**
264
     * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
265
     *
266
     * @param title
267
     * @param source
268
     * @param message
269
     * @param pluginId
270
     * @param t
271
     */
272
    public static void errorDialog(final String title,
273
            final Object source,
274
            final String message,
275
            final String pluginId,
276
            final Throwable t,
277
            boolean addContactMesg) {
278

    
279
        Throwable throwable = t;
280
        StringBuffer sbStackTrace = new StringBuffer();
281

    
282
        // We need to build a MultiStatus object since the simple
283
        // idea of writing out the stack trace as a single string
284
        // leads to a single line on windows
285
        List<Status> childStatuses = new ArrayList<Status>();
286

    
287
        // add context info
288
        List<String> contextInfo = getContextInfo();
289
        for(String infoItem : contextInfo) {
290
            childStatuses.add(new Status(IStatus.ERROR, pluginId, infoItem));
291
        }
292

    
293
        if(throwable == null) {
294
            throwable = getDefaultThrowable();
295
        }
296

    
297
        int thCount = 0;
298
        int maxTraces = 4;
299

    
300
        for(Throwable th : ExceptionUtils.getThrowables(throwable)) {
301
            // add main exception
302
            if(thCount == 0) {
303
                for (StackTraceElement ste : th.getStackTrace()) {
304
                    childStatuses.add(new Status(IStatus.ERROR, pluginId, "  at " + ste.toString()));
305
                }
306
            } else {
307
                // add recursive causes
308
                if(th != null) {
309
                    childStatuses.add(new Status(IStatus.ERROR, pluginId, ""));
310
                    String msg = th.toString();
311
                    childStatuses.add(new Status(IStatus.ERROR, pluginId, "Caused by : " + msg));
312
                    int traceCount = 0;
313
                    for (StackTraceElement ste : th.getStackTrace()) {
314
                        // add only pre-defined number of trace elements
315
                        if(traceCount > maxTraces) {
316
                            childStatuses.add(new Status(IStatus.ERROR, pluginId, "  ...."));
317
                            break;
318
                        }
319
                        // build & add status
320
                        childStatuses.add(new Status(IStatus.ERROR, pluginId, "  at " + ste.toString()));
321
                        traceCount++;
322
                    }
323
                }
324
            }
325
            thCount++;
326
        }
327
        String finalMessage = message;
328

    
329
        if(finalMessage == null || finalMessage.isEmpty()) {
330
            finalMessage = DEFAULT_MESSAGE;
331
        }
332

    
333
        if(addContactMesg) {
334
            // add edit support contact info to message
335
            finalMessage += MessagingUtils.CONTACT_MESSAGE;
336
        }
337

    
338
        MultiStatus ms = new MultiStatus(pluginId,
339
                IStatus.ERROR,
340
                childStatuses.toArray(new Status[] {}),
341
                throwable.toString(),
342
                throwable);
343

    
344
        errorDialog(title, source, throwable, contextInfo, finalMessage, ms);
345
    }
346

    
347
    /**
348
     * Displays a dialog for an exception occurring in an operation.
349
     *
350
     * This will be either a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog} in case of a
351
     * security runtime exception or a warning {@link org.eclipse.jface.dialogs.MessageDialog} in
352
     * case of any other exception.
353
     *
354
     * @param title
355
     *            a {@link java.lang.String} object.
356
     * @param source
357
     *            a {@link java.lang.Object} object.
358
     * @param status
359
     *            a {@link org.eclipse.core.runtime.IStatus} object.
360
     */
361
    public static void operationDialog(final Object source,
362
            final Exception ex,
363
            final String pluginId,
364
            final String operationlabel,
365
            final String hint) {
366

    
367
        Display.getDefault().asyncExec(new Runnable() {
368

    
369
            @Override
370
            public void run() {
371
                MultiStatus info = null;
372
                String title = null;
373

    
374
                // FIXME cannot access TaxonomicEditorPlugin.PLUGIN_ID from here
375
                // String PID = TaxonomicEditorPlugin.PLUGIN_ID;
376
                String PID = "eu.etaxonomy.taxeditor.application";
377

    
378
                // checking security exceptions for every operation
379
                RuntimeException securityRuntimeException = SecurityExceptionUtils.findSecurityRuntimeException(ex);
380

    
381
                // in case of a security exception it is a warning, else it is an error
382
                if(securityRuntimeException != null){
383
                    title = "Your changes could not be saved!";
384
                    warningDialog(title, source, String.format("You are missing sufficient permissions for the operation \"%s\". %s", operationlabel, hint));
385
                } else {
386
                    title = "Error executing operation";
387
                    errorDialog(title, source, String.format("An error occured while executing %s. %s", operationlabel, hint), pluginId, ex, true);
388

    
389
                }
390

    
391

    
392
            }
393
        });
394
    }
395

    
396

    
397

    
398

    
399
    /**
400
     * Displays a question {@link org.eclipse.jface.dialogs.MessageDialog}.
401
     *
402
     * @param title
403
     *            a {@link java.lang.String} object.
404
     * @param message
405
     *            a {@link java.lang.String} object.
406
     * @return a boolean.
407
     */
408
    public static boolean confirmDialog(String title, String message) {
409
        return MessageDialog.openQuestion(AbstractUtility.getShell(), title, message);
410
    }
411

    
412
    /**
413
     * Displays a message {@link org.eclipse.jface.dialogs.MessageDialog}.
414
     *
415
     * @param title
416
     * @param source
417
     * @param message
418
     */
419
    public static void messageDialog(final String title, final Object source, final String message) {
420
        MessagingUtils.messageDialog(title, source, message, null, true);
421
    }
422

    
423

    
424

    
425
    /**
426
     * Displays an error {@link org.eclipse.jface.dialogs.MessageDialog}.
427
     *
428
     * @param title
429
     *            The dialogs title
430
     * @param source
431
     *            The object where the warning was generated (used by log4j)
432
     * @param message
433
     *            An informative String to be presented to the user
434
     * @param title
435
     *            The dialogs title
436
     * @param t
437
     *            a Throwable if one exists or null
438
     */
439
    public static void messageDialog(final String title,
440
            final Object source,
441
            final String message,
442
            final Throwable t) {
443
        MessagingUtils.messageDialog(title, source, message, t, true);
444
    }
445

    
446
    /**
447
     * Displays an error {@link org.eclipse.jface.dialogs.MessageDialog}.
448
     *
449
     * @param title
450
     *            The dialogs title
451
     * @param source
452
     *            The object where the warning was generated (used by log4j)
453
     * @param message
454
     *            An informative String to be presented to the user
455
     * @param title
456
     *            The dialogs title
457
     * @param t
458
     *            a Throwable if one exists or null
459
     */
460
    public static void messageDialog(final String title,
461
            final Object source,
462
            final String message,
463
            final Throwable t,
464
            boolean async) {
465
        if(async) {
466
            Display.getDefault().asyncExec(new Runnable() {
467

    
468
                @Override
469
                public void run() {
470
                    MessageDialog.openError(AbstractUtility.getShell(), title, message + getCauseRecursively(t));
471
                    Class<? extends Object> clazz = source != null ? source
472
                            .getClass() : this.getClass();
473
                            error(clazz, message, t);
474
                }
475

    
476

    
477
            });
478
        } else {
479
            MessageDialog.openError(AbstractUtility.getShell(), title, message + getCauseRecursively(t));
480
            Class<? extends Object> clazz = source != null ? source.getClass() : TaxeditorStorePlugin.class;
481
            error(clazz, message, t);
482
        }
483
    }
484

    
485
    public static String getCauseRecursively(Throwable t) {
486
        if(t == null){
487
            return "";
488
        }
489

    
490
        if(t.getCause() != null){
491
            return getCauseRecursively(t.getCause());
492
        }else{
493
            return String.format("\n\nException: %s\nMessage: %s", t.getClass().getSimpleName(), t.getMessage());
494
        }
495

    
496
    }
497
    /**
498
     * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
499
     *
500
     * @param title
501
     * @param termBase
502
     * @param status
503
     */
504
    public static void warningDialog(String title, Object source,
505
            IStatus status) {
506
        MessagingUtils.warningDialog(title, source, status.getMessage());
507
    }
508

    
509
    /**
510
     * Standard warning dialog for the case when the application is not yet connected to the datasource
511
     *
512
     * @param source
513
     */
514
    public static void noDataSourceWarningDialog(Object source) {
515
        MessagingUtils
516
        .warningDialog(
517
                "Application is not connected to a datastore",
518
                source,
519
                "The requested operation is only available when "
520
                + "connected to a datasource. You may choose a datasource to connect to or create a new one in the datasource view.");
521
    }
522

    
523
    /**
524
     * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
525
     *
526
     * @param title
527
     *            The dialogs title
528
     * @param source
529
     *            The object where the warning was generated (used by log4j)
530
     * @param message
531
     *            An informative String to be presented to the user
532
     */
533
    public static void warningDialog(final String title, final Object source, final String message) {
534
        Display.getDefault().asyncExec(new Runnable() {
535

    
536
            @Override
537
            public void run() {
538
                MessageDialog.openWarning(AbstractUtility.getShell(), title, message);
539
                Class<? extends Object> clazz = source != null ? source
540
                        .getClass() : AbstractUtility.class;
541
                        warn(clazz, message);
542
            }
543
        });
544
    }
545

    
546
    /**
547
     * Displays an information {@link org.eclipse.jface.dialogs.MessageDialog}.
548
     *
549
     * @param title
550
     * @param status
551
     */
552
    public static void informationDialog(final String title, final IStatus status) {
553
        MessagingUtils.informationDialog(title, status.getMessage());
554
    }
555

    
556
    /**
557
     * Displays an information {@link org.eclipse.jface.dialogs.MessageDialog}.
558
     *
559
     * @param title
560
     *            a {@link java.lang.String} object.
561
     * @param message
562
     *            a {@link java.lang.String} object.
563
     */
564
    public static void informationDialog(final String title,
565
            final String message) {
566
        Display.getDefault().asyncExec(new Runnable() {
567

    
568
            @Override
569
            public void run() {
570
                MessageDialog.openInformation(AbstractUtility.getShell(), title, message);
571
            }
572
        });
573
    }
574

    
575
    /**
576
     * Open a message box that informs the user about unimplemented
577
     * functionality. This method is for developer convenience.
578
     *
579
     * @param source
580
     *            a {@link java.lang.Object} object.
581
     */
582
    public static void notImplementedMessage(Object source) {
583
        warningDialog("Not yet implemented", source,
584
                "This functionality is not yet implemented.");
585
    }
586

    
587
}
(28-28/37)