Project

General

Profile

Download (22.8 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.time.LocalDateTime;
6
import java.time.format.DateTimeFormatter;
7
import java.util.ArrayList;
8
import java.util.List;
9

    
10
import org.apache.commons.lang.exception.ExceptionUtils;
11
import org.apache.log4j.Logger;
12
import org.eclipse.core.runtime.IStatus;
13
import org.eclipse.core.runtime.MultiStatus;
14
import org.eclipse.core.runtime.Platform;
15
import org.eclipse.core.runtime.Status;
16
import org.eclipse.jface.dialogs.MessageDialog;
17
import org.eclipse.swt.widgets.Display;
18

    
19
import eu.etaxonomy.cdm.common.CdmUtils;
20
import eu.etaxonomy.cdm.config.ICdmSource;
21
import eu.etaxonomy.cdm.test.integration.SecurityExceptionUtils;
22
import eu.etaxonomy.taxeditor.remoting.RemoteExecutionTimestampsUtil;
23
import eu.etaxonomy.taxeditor.remoting.source.CdmRemoteSource;
24
import eu.etaxonomy.taxeditor.store.CdmStore;
25
import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
26

    
27
/**
28
 * Utility class which handles all the messaging information generated by the
29
 * Editor.
30
 *
31
 * This includes logging as well as dialogs.
32
 *
33
 * @author cmathew
34
 *
35
 */
36
public class MessagingUtils {
37
    public final static String UNEXPECTED_ERROR_MESSAGE = "This is an unexpected error.";
38
    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).";
39
    public final static String DEFAULT_MESSAGE = "Error thrown but no associated message";
40
    public final static String CONNECTION_FAILURE_MESSAGE = "The connection to the remote server has been broken";
41
    public final static String REMOTE_ACCESS_FAILURE_MESSAGE = "Maybe the server is currently not available. If the problem persists please contact the server admin with the error trace below.";
42
    public static final String WIDGET_IS_DISPOSED_MESSAGE = "A widget was called, which was already disposed";
43
    public static final String ACCESS_DENIED = "The connection to the server could not be established because the access was denied.";
44
    /**
45
     * Gets the Log4J logger for a given class
46
     *
47
     * @param clazz
48
     *            a {@link java.lang.Class} object.
49
     * @return a {@link org.apache.log4j.Logger} object.
50
     */
51
    public static Logger getLog4JLogger(Class clazz) {
52
        return Logger.getLogger(clazz);
53
    }
54

    
55
    /**
56
     * Logs details from a given Status object
57
     *
58
     * @param status
59
     * 				a {@link org.eclipse.core.runtime.IStatus} object.
60
     */
61
    private static void log(IStatus status) {
62
        TaxeditorStorePlugin.getDefault().getLog().log(status);
63
    }
64

    
65
    /**
66
     * Logs a status object as information.
67
     *
68
     * @param status
69
     *            a {@link org.eclipse.core.runtime.IStatus} object.
70
     */
71
    public static void info(IStatus status) {
72
        log(status);
73
    }
74

    
75
    /**
76
     * Logs a string as information.
77
     *
78
     * @param message
79
     *            a {@link java.lang.String} object.
80
     */
81
    public static void info(String message) {
82
        IStatus status = new Status(IStatus.INFO, AbstractUtility.getPluginId(), message);
83
        info(status);
84
    }
85

    
86
    /**
87
     * Logs an exception from a given source as a warning.
88
     *
89
     * @param source
90
     * @param t
91
     */
92
    public static void warn(Class source, Throwable t) {
93
        IStatus status = new Status(IStatus.WARNING, AbstractUtility.getPluginId(), t.getMessage(), t);
94
        MessagingUtils.getLog4JLogger(source).warn(t);
95
        log(status);
96
    }
97

    
98
    /**
99
     * Logs a status object from a given source as a warning.
100
     *
101
     * @param source
102
     * @param status
103
     */
104
    public static void warn(Class source, IStatus status) {
105
        MessagingUtils.getLog4JLogger(source).warn(status.getMessage(), status.getException());
106
        log(status);
107
    }
108

    
109
    /**
110
     * Logs a string from a given source as a warning.
111
     *
112
     *
113
     * @param source
114
     *            a {@link java.lang.Class} object.
115
     * @param message
116
     *            a {@link java.lang.String} object.
117
     */
118
    public static void warn(Class source, String message) {
119
        IStatus status = new Status(IStatus.WARNING, AbstractUtility.getPluginId(), message);
120
        MessagingUtils.getLog4JLogger(source).warn(message);
121
        log(status);
122
    }
123

    
124
    /**
125
     * Logs a status object from a given source as an error.
126
     *
127
     *
128
     * @param source
129
     *            a {@link java.lang.Class} object.
130
     * @param status
131
     *            a {@link org.eclipse.core.runtime.IStatus} object.
132
     */
133
    public static void error(Class source, IStatus status) {
134
        getLog4JLogger(source)
135
        .error(status.getMessage(), status.getException());
136
        log(status);
137
    }
138

    
139
    /**
140
     * Logs a string and exception from a given source as an error.
141
     *
142
     *
143
     * @param source
144
     *            a {@link java.lang.Class} object.
145
     * @param message
146
     *            a {@link java.lang.String} object.
147
     * @param t
148
     *            a {@link java.lang.Throwable} object.
149
     */
150
    public static void error(Class source, String message, Throwable t) {
151
        IStatus status = new Status(IStatus.ERROR, AbstractUtility.getPluginId(), message, t);
152
        error(source, status);
153
    }
154

    
155

    
156

    
157
    /**
158
     * Logs an exception from a given source as an error.
159
     *
160
     *
161
     * @param source
162
     *            a {@link java.lang.Class} object.
163
     * @param t
164
     *            a {@link java.lang.Throwable} object.
165
     */
166
    public static void error(Class source, Throwable t) {
167
        error(source.getClass(), t.getMessage(), t);
168
    }
169

    
170

    
171

    
172
    /**
173
     * Returns a list of strings, providing info on,
174
     *  - login
175
     *  - editor version
176
     *  - server (address + source name)
177
     *  - db schema version
178
     *
179
     * @return
180
     */
181
    public static List<String> getContextInfo() {
182
        List<String> contextInfo = new ArrayList<String>();
183
        String name = "";
184
        String contextPath = "";
185
        String schemaVersion = "";
186
        String server = "";
187
        String version = "";
188
        String login = "";
189
        try {
190
            version = Platform.getBundle("eu.etaxonomy.taxeditor.application").getHeaders().get(org.osgi.framework.Constants.BUNDLE_VERSION);
191

    
192
            ICdmSource activeCdmSource = CdmStore.getActiveCdmSource();
193
            if(activeCdmSource != null ) {
194
                login = CdmStore.getLoginManager().getAuthenticatedUser().getUsername();
195
                name = activeCdmSource.getName();
196
                schemaVersion = activeCdmSource.getDbSchemaVersion();
197
                server = activeCdmSource.getServer();
198
                if(activeCdmSource instanceof CdmRemoteSource){
199
                    contextPath = ((CdmRemoteSource) activeCdmSource).getContextPath();
200
                    if (contextPath != null && contextPath.startsWith("cdmserver/")){
201
                    	contextPath = contextPath.substring("cdmserver/".length());
202
                    }
203
                }
204
            }
205

    
206
        } catch (Exception e) {
207
            // Nothing to do
208
        }
209

    
210
        // add time stamps
211
        LocalDateTime date = LocalDateTime.now();
212
        String dateTimeStr = date.format(DateTimeFormatter.ISO_DATE_TIME);
213

    
214
        if(RemoteExecutionTimestampsUtil.getLastServiceMethod() != null){
215
            contextInfo.add("last remote method : " + RemoteExecutionTimestampsUtil.getLastServiceMethod());
216
        }
217
        if(RemoteExecutionTimestampsUtil.getLastRequestClientTime() != null){
218
            contextInfo.add("last remote request client time : " + RemoteExecutionTimestampsUtil.getLastRequestClientTime());
219
        }
220
        if(RemoteExecutionTimestampsUtil.getLastResponseHttpHeaderTime()!= null){
221
            contextInfo.add("last remote request response header time : " + RemoteExecutionTimestampsUtil.getLastResponseHttpHeaderTime());
222
        }
223
        contextInfo.add("client error time : " + dateTimeStr);
224
        contextInfo.add("login : " + login);
225
        contextInfo.add("editor version : " + version);
226
        contextInfo.add("server : " + server + " (" + name + ")" + (CdmUtils.isNotBlank(contextPath)?" / "+contextPath:""));
227
        contextInfo.add("schema version : " + schemaVersion);
228
        contextInfo.add("os : " + System.getProperty("os.name")+" "+System.getProperty("os.version")+" "+System.getProperty("os.arch"));
229
        contextInfo.add("java : "+System.getProperty("java.version"));
230

    
231
        return contextInfo;
232
    }
233

    
234
    public static String getStackTraceAndContextInfo(Throwable t, List<String> contextInfo)  {
235
        StringBuffer stackTraceAndContextInfo = new StringBuffer();
236
        Throwable throwable = t;
237

    
238
        for(String infoItem : contextInfo) {
239
            stackTraceAndContextInfo.append(infoItem + System.getProperty("line.separator"));
240
        }
241

    
242
        StringWriter sw = new StringWriter();
243

    
244
        if(throwable == null) {
245
            throwable = getDefaultThrowable();
246
        }
247
        throwable.printStackTrace(new PrintWriter(sw));
248

    
249
        stackTraceAndContextInfo.append(sw.toString());
250

    
251
        return stackTraceAndContextInfo.toString();
252
    }
253

    
254
    public static String getContextInfo(List<String> contextInfo)  {
255
        StringBuffer scontextInfoStringBuffer = new StringBuffer();
256

    
257

    
258
        for(String infoItem : contextInfo) {
259
            scontextInfoStringBuffer.append(infoItem + System.getProperty("line.separator"));
260
        }
261

    
262

    
263

    
264
        return scontextInfoStringBuffer.toString();
265
    }
266

    
267
    private static Throwable getDefaultThrowable() {
268
        return new Throwable("Error thrown but no associated exception");
269
    }
270

    
271

    
272

    
273
    /**
274
     * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
275
     *
276
     * @param title
277
     * @param source
278
     * @param t
279
     * @param contextInfo
280
     * @param message
281
     * @param status
282
     */
283
    private static void errorDialog(final String title,
284
            final Object source,
285
            final Throwable t,
286
            final List<String> contextInfo,
287
            final String message,
288
            final MultiStatus status,
289
            final boolean showReason) {
290

    
291
        Display.getDefault().asyncExec(new Runnable() {
292

    
293
            @Override
294
            public void run() {
295
                String stackTraceWithContext = getStackTraceAndContextInfo(t, contextInfo);
296
                CdmErrorDialog ced = new CdmErrorDialog(AbstractUtility.getShell(), title, message, status, stackTraceWithContext, showReason);
297
                ced.open();
298
                Class<? extends Object> clazz = source != null ? source.getClass() : this.getClass();
299

    
300

    
301
                IStatus singleStatus = new Status(IStatus.ERROR,
302
                        status.getPlugin(),
303
                        message,
304
                        new Exception(stackTraceWithContext));
305

    
306
                error(clazz, singleStatus);
307
            }
308
        });
309
    }
310

    
311
    public static void errorDialog(final String title,
312
            final Object source,
313
            final String message,
314
            final String pluginId,
315
            final Throwable t,
316
            boolean addContactMesg) {
317
        errorDialog(title, source, message, pluginId, t, addContactMesg, true);
318

    
319
    }
320
    /**
321
     * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
322
     *
323
     * @param title
324
     * @param source
325
     * @param message
326
     * @param pluginId
327
     * @param t
328
     */
329
    public static void errorDialog(final String title,
330
            final Object source,
331
            final String message,
332
            final String pluginId,
333
            final Throwable t,
334
            boolean addContactMesg,
335
            boolean showReason) {
336

    
337
        Throwable throwable = t;
338

    
339
        // We need to build a MultiStatus object since the simple
340
        // idea of writing out the stack trace as a single string
341
        // leads to a single line on windows
342
        List<Status> childStatuses = new ArrayList<Status>();
343

    
344
        // add context info
345
        List<String> contextInfo = getContextInfo();
346
        for(String infoItem : contextInfo) {
347
            childStatuses.add(new Status(IStatus.ERROR, pluginId, infoItem));
348
        }
349

    
350
        if(throwable == null) {
351
            throwable = getDefaultThrowable();
352
        }
353

    
354
        int thCount = 0;
355
        int maxTraces = 4;
356

    
357
        for(Throwable th : ExceptionUtils.getThrowables(throwable)) {
358
            // add main exception
359
            if(thCount == 0) {
360
                for (StackTraceElement ste : th.getStackTrace()) {
361
                    childStatuses.add(new Status(IStatus.ERROR, pluginId, "  at " + ste.toString()));
362
                }
363
            } else {
364
                // add recursive causes
365
                if(th != null) {
366
                    childStatuses.add(new Status(IStatus.ERROR, pluginId, ""));
367
                    String msg = th.toString();
368
                    childStatuses.add(new Status(IStatus.ERROR, pluginId, "Caused by : " + msg));
369
                    int traceCount = 0;
370
                    for (StackTraceElement ste : th.getStackTrace()) {
371
                        // add only pre-defined number of trace elements
372
                        if(traceCount > maxTraces) {
373
                            childStatuses.add(new Status(IStatus.ERROR, pluginId, "  ...."));
374
                            break;
375
                        }
376
                        // build & add status
377
                        childStatuses.add(new Status(IStatus.ERROR, pluginId, "  at " + ste.toString()));
378
                        traceCount++;
379
                    }
380
                }
381
            }
382
            thCount++;
383
        }
384
        String finalMessage = message;
385

    
386
        if(finalMessage == null || finalMessage.isEmpty()) {
387
            finalMessage = DEFAULT_MESSAGE;
388
        }
389

    
390
        if(addContactMesg) {
391
            // add edit support contact info to message
392
            finalMessage += MessagingUtils.CONTACT_MESSAGE;
393
        }
394

    
395
        MultiStatus ms = new MultiStatus(pluginId,
396
                IStatus.ERROR,
397
                childStatuses.toArray(new Status[] {}),
398
                throwable.toString(),
399
                throwable);
400

    
401
        errorDialog(title, source, throwable, contextInfo, finalMessage, ms, showReason);
402
    }
403

    
404
    /**
405
     * Displays a dialog for an exception occurring in an operation.
406
     *
407
     * This will be either a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog} in case of a
408
     * security runtime exception or a warning {@link org.eclipse.jface.dialogs.MessageDialog} in
409
     * case of any other exception.
410
     *
411
     * @param title
412
     *            a {@link java.lang.String} object.
413
     * @param source
414
     *            a {@link java.lang.Object} object.
415
     * @param status
416
     *            a {@link org.eclipse.core.runtime.IStatus} object.
417
     */
418
    public static void operationDialog(final Object source,
419
            final Exception ex,
420
            final String pluginId,
421
            final String operationlabel,
422
            final String hint) {
423

    
424
        Display.getDefault().asyncExec(new Runnable() {
425

    
426
            @Override
427
            public void run() {
428

    
429
                String title = null;
430

    
431
                // FIXME cannot access TaxonomicEditorPlugin.PLUGIN_ID from here
432
                // FIXME is there any reason for keeping the lines below?
433
                // String PID = TaxonomicEditorPlugin.PLUGIN_ID;
434
                // String PID = "eu.etaxonomy.taxeditor.application";
435

    
436
                // checking security exceptions for every operation
437
                RuntimeException securityRuntimeException = SecurityExceptionUtils.findSecurityRuntimeException(ex);
438

    
439
                // in case of a security exception it is a warning, else it is an error
440
                if(securityRuntimeException != null){
441
                    title = "Your changes could not be saved!";
442
                    warningDialog(title, source, String.format("You are missing sufficient permissions for the operation \"%s\". %s", operationlabel, hint));
443
                } else {
444
                    title = "Error executing operation";
445
                    errorDialog(title, source, String.format("An error occurred while executing %s. %s", operationlabel, hint), pluginId, ex, true);
446

    
447
                }
448

    
449

    
450
            }
451
        });
452
    }
453

    
454

    
455

    
456

    
457
    /**
458
     * Displays a question {@link org.eclipse.jface.dialogs.MessageDialog}.
459
     *
460
     * @param title
461
     *            a {@link java.lang.String} object.
462
     * @param message
463
     *            a {@link java.lang.String} object.
464
     * @return a boolean.
465
     */
466
    public static boolean confirmDialog(String title, String message) {
467
        return MessageDialog.openQuestion(AbstractUtility.getShell(), title, message);
468
    }
469

    
470
    public static int confirmDialog(String title, String message, String...labels){
471
        MessageDialog dialog =new MessageDialog(AbstractUtility.getShell(), title, null, message, MessageDialog.QUESTION,labels, 0);
472
        int result = dialog.open();
473
        return result;
474
    }
475

    
476
    /**
477
     * Displays a message {@link org.eclipse.jface.dialogs.MessageDialog}.
478
     *
479
     * @param title
480
     * @param source
481
     * @param message
482
     */
483
    public static void messageDialog(final String title, final Object source, final String message) {
484
        MessagingUtils.messageDialog(title, source, message, null, true);
485
    }
486

    
487

    
488

    
489
    /**
490
     * Displays an error {@link org.eclipse.jface.dialogs.MessageDialog}.
491
     *
492
     * @param title
493
     *            The dialogs title
494
     * @param source
495
     *            The object where the warning was generated (used by log4j)
496
     * @param message
497
     *            An informative String to be presented to the user
498
     * @param title
499
     *            The dialogs title
500
     * @param t
501
     *            a Throwable if one exists or null
502
     */
503
    public static void messageDialog(final String title,
504
            final Object source,
505
            final String message,
506
            final Throwable t) {
507
        MessagingUtils.messageDialog(title, source, message, t, true);
508
    }
509

    
510
    /**
511
     * Displays an error {@link org.eclipse.jface.dialogs.MessageDialog}.
512
     *
513
     * @param title
514
     *            The dialogs title
515
     * @param source
516
     *            The object where the warning was generated (used by log4j)
517
     * @param message
518
     *            An informative String to be presented to the user
519
     * @param title
520
     *            The dialogs title
521
     * @param t
522
     *            a Throwable if one exists or null
523
     */
524
    public static void messageDialog(final String title,
525
            final Object source,
526
            final String message,
527
            final Throwable t,
528
            boolean async) {
529
        if(async) {
530
            Display.getDefault().asyncExec(new Runnable() {
531

    
532
                @Override
533
                public void run() {
534
                    MessageDialog.openError(AbstractUtility.getShell(), title, message + getCauseRecursively(t));
535
                    Class<? extends Object> clazz = source != null ? source
536
                            .getClass() : this.getClass();
537
                            error(clazz, message, t);
538
                }
539

    
540

    
541
            });
542
        } else {
543
            MessageDialog.openError(AbstractUtility.getShell(), title, message + getCauseRecursively(t));
544
            Class<? extends Object> clazz = source != null ? source.getClass() : TaxeditorStorePlugin.class;
545
            error(clazz, message, t);
546
        }
547
    }
548

    
549
    public static String getCauseRecursively(Throwable t) {
550
        if(t == null){
551
            return "";
552
        }
553

    
554
        if(t.getCause() != null){
555
            return getCauseRecursively(t.getCause());
556
        }else{
557
            return String.format("\n\nException: %s\nMessage: %s", t.getClass().getSimpleName(), t.getMessage());
558
        }
559

    
560
    }
561
    /**
562
     * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
563
     *
564
     * @param title
565
     * @param termBase
566
     * @param status
567
     */
568
    public static void warningDialog(String title, Object source,
569
            IStatus status) {
570
        MessagingUtils.warningDialog(title, source, status.getMessage());
571
    }
572

    
573
    /**
574
     * Standard warning dialog for the case when the application is not yet connected to the datasource
575
     *
576
     * @param source
577
     */
578
    public static void noDataSourceWarningDialog(Object source) {
579
        MessagingUtils
580
        .warningDialog(
581
                "Application is not connected to a datastore",
582
                source,
583
                "The requested operation is only available when "
584
                + "connected to a datasource. You may choose a datasource to connect to or create a new one in the datasource view.");
585
    }
586

    
587
    /**
588
     * Standard warning dialog for the case when the datasource is not available
589
     *
590
     * @param source
591
     */
592
    public static void dataSourceNotAvailableWarningDialog(Object source) {
593
        MessagingUtils
594
        .warningDialog(
595
                "The datasource is not available",
596
                source,
597
                "The editor is not connected to a datasource. Maybe the datasource is not available.");
598
    }
599

    
600

    
601
    /**
602
     * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
603
     *
604
     * @param title
605
     *            The dialogs title
606
     * @param source
607
     *            The object where the warning was generated (used by log4j)
608
     * @param message
609
     *            An informative String to be presented to the user
610
     */
611
    public static void warningDialog(final String title, final Object source, final String message) {
612
        Display.getDefault().asyncExec(new Runnable() {
613

    
614
            @Override
615
            public void run() {
616
                MessageDialog.openWarning(AbstractUtility.getShell(), title, message);
617
                Class<? extends Object> clazz = source != null ? source
618
                        .getClass() : AbstractUtility.class;
619
                        warn(clazz, message);
620
            }
621
        });
622
    }
623

    
624
    /**
625
     * Displays an information {@link org.eclipse.jface.dialogs.MessageDialog}.
626
     *
627
     * @param title
628
     * @param status
629
     */
630
    public static void informationDialog(final String title, final IStatus status) {
631
        MessagingUtils.informationDialog(title, status.getMessage());
632
    }
633

    
634
    /**
635
     * Displays an information {@link org.eclipse.jface.dialogs.MessageDialog}.
636
     *
637
     * @param title
638
     *            a {@link java.lang.String} object.
639
     * @param message
640
     *            a {@link java.lang.String} object.
641
     */
642
    public static void informationDialog(final String title,
643
            final String message) {
644
        Display.getDefault().asyncExec(new Runnable() {
645

    
646
            @Override
647
            public void run() {
648
                MessageDialog.openInformation(AbstractUtility.getShell(), title, message);
649
            }
650
        });
651
    }
652

    
653
    /**
654
     * Open a message box that informs the user about unimplemented
655
     * functionality. This method is for developer convenience.
656
     *
657
     * @param source
658
     *            a {@link java.lang.Object} object.
659
     */
660
    public static void notImplementedMessage(Object source) {
661
        warningDialog("Not yet implemented", source,
662
                "This functionality is not yet implemented.");
663
    }
664

    
665
}
(32-32/41)