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.lang3.exception.ExceptionUtils;
11
import org.apache.logging.log4j.LogManager;
12
import org.apache.logging.log4j.Logger;
13
import org.eclipse.core.runtime.IStatus;
14
import org.eclipse.core.runtime.MultiStatus;
15
import org.eclipse.core.runtime.Platform;
16
import org.eclipse.core.runtime.Status;
17
import org.eclipse.jface.dialogs.MessageDialog;
18
import org.eclipse.swt.widgets.Display;
19

    
20
import eu.etaxonomy.cdm.common.CdmUtils;
21
import eu.etaxonomy.cdm.config.ICdmSource;
22
import eu.etaxonomy.cdm.persistence.permission.SecurityExceptionUtils;
23
import eu.etaxonomy.taxeditor.remoting.RemoteExecutionTimestampsUtil;
24
import eu.etaxonomy.taxeditor.remoting.source.ICdmRemoteSource;
25
import eu.etaxonomy.taxeditor.store.CdmStore;
26
import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
27

    
28
/**
29
 * Utility class which handles all the messaging information generated by the
30
 * Editor.
31
 *
32
 * This includes logging as well as dialogs.
33
 *
34
 * @author cmathew
35
 */
36
public class MessagingUtils {
37

    
38
    public final static String UNEXPECTED_ERROR_MESSAGE = "An error occurred.";
39
    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).";
40
    public final static String DEFAULT_MESSAGE = "Error thrown but no associated message";
41
    public final static String CONNECTION_FAILURE_MESSAGE = "The connection to the remote server has been broken";
42
    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.";
43
    public static final String WIDGET_IS_DISPOSED_MESSAGE = "A widget was called, which was already disposed";
44
    public static final String ACCESS_DENIED = "The connection to the server could not be established because the access was denied.";
45
    public static final String PERMISSION_DENIED = "You do not have the permission to ";
46
    public static final String RESTART_EDITOR_MESSAGE = "An error occured that may require restarting the editor.";
47
    /**
48
     * Gets the Log4J logger for a given class
49
     *
50
     * @param clazz
51
     *            a {@link java.lang.Class} object.
52
     * @return a {@link org.apache.logging.log4j.Logger} object.
53
     */
54
    public static Logger getLog4JLogger(Class<?> clazz) {
55
        return LogManager.getLogger(clazz);
56
    }
57

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

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

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

    
89
    /**
90
     * Logs an exception from a given source as a warning.
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
    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
     * Logs an exception from a given source as an error.
154
     *
155
     *
156
     * @param source
157
     *            a {@link java.lang.Class} object.
158
     * @param t
159
     *            a {@link java.lang.Throwable} object.
160
     */
161
    public static void error(Class<?> source, Throwable t) {
162
        error(source.getClass(), t.getMessage(), t);
163
    }
164

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

    
185
            ICdmSource activeCdmSource = CdmStore.getActiveCdmSource();
186
            if(activeCdmSource != null ) {
187
                login = CdmStore.getLoginManager().getAuthenticatedUser().getUsername();
188
                name = activeCdmSource.getName();
189
                schemaVersion = activeCdmSource.getDbSchemaVersion();
190
                server = activeCdmSource.getServer();
191
                if(activeCdmSource instanceof ICdmRemoteSource){
192
                    contextPath = ((ICdmRemoteSource)activeCdmSource).getContext();
193
                    if (contextPath != null && contextPath.startsWith("cdmserver/")){
194
                    	contextPath = contextPath.substring("cdmserver/".length());
195
                    }
196
                }
197
            }
198

    
199
        } catch (Exception e) {
200
            // Nothing to do
201
        }
202

    
203
        // add time stamps
204
        LocalDateTime date = LocalDateTime.now();
205
        String dateTimeStr = date.format(DateTimeFormatter.ISO_DATE_TIME);
206

    
207
        if(RemoteExecutionTimestampsUtil.getLastService() != null){
208
            contextInfo.add("last remote service : " + RemoteExecutionTimestampsUtil.getLastService());
209
        }
210
        if(RemoteExecutionTimestampsUtil.getLastMethod() != null){
211
            contextInfo.add("last remote method : " + RemoteExecutionTimestampsUtil.getLastMethod());
212
        }
213
        if(RemoteExecutionTimestampsUtil.getLastRequestClientTime() != null){
214
            contextInfo.add("last remote request client time : " + RemoteExecutionTimestampsUtil.getLastRequestClientTime());
215
        }
216
        if(RemoteExecutionTimestampsUtil.getLastResponseHttpHeaderTime()!= null){
217
            contextInfo.add("last remote request response header time : " + RemoteExecutionTimestampsUtil.getLastResponseHttpHeaderTime());
218
        }
219
        contextInfo.add("client error time : " + dateTimeStr);
220
        contextInfo.add("login : " + login);
221
        contextInfo.add("editor version : " + version);
222
        contextInfo.add("server : " + server + " (" + name + ")" + (CdmUtils.isNotBlank(contextPath)?" / "+contextPath:""));
223
        contextInfo.add("schema version : " + schemaVersion);
224
        contextInfo.add("os : " + System.getProperty("os.name")+" "+System.getProperty("os.version")+" "+System.getProperty("os.arch"));
225
        contextInfo.add("java : "+System.getProperty("java.version"));
226

    
227
        return contextInfo;
228
    }
229

    
230
    public static String getStackTraceAndContextInfo(Throwable t, List<String> contextInfo)  {
231
        StringBuffer stackTraceAndContextInfo = new StringBuffer();
232
        Throwable throwable = t;
233

    
234
        for(String infoItem : contextInfo) {
235
            stackTraceAndContextInfo.append(infoItem + System.getProperty("line.separator"));
236
        }
237

    
238
        StringWriter sw = new StringWriter();
239

    
240
        if(throwable == null) {
241
            throwable = getDefaultThrowable();
242
        }
243
        throwable.printStackTrace(new PrintWriter(sw));
244

    
245
        stackTraceAndContextInfo.append(sw.toString());
246

    
247
        return stackTraceAndContextInfo.toString();
248
    }
249

    
250
    public static String getContextInfo(List<String> contextInfo)  {
251
        StringBuffer scontextInfoStringBuffer = new StringBuffer();
252

    
253
        for(String infoItem : contextInfo) {
254
            scontextInfoStringBuffer.append(infoItem + System.getProperty("line.separator"));
255
        }
256

    
257
        return scontextInfoStringBuffer.toString();
258
    }
259

    
260
    private static Throwable getDefaultThrowable() {
261
        return new Throwable("Error thrown but no associated exception");
262
    }
263

    
264
    /**
265
     * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
266
     *
267
     */
268
    private static void errorDialog(final String title,
269
            final Object source,
270
            final Throwable t,
271
            final List<String> contextInfo,
272
            final String message,
273
            final MultiStatus status,
274
            final boolean showReason) {
275

    
276
        Display.getDefault().asyncExec(new Runnable() {
277

    
278
            @Override
279
            public void run() {
280
                String stackTraceWithContext = getStackTraceAndContextInfo(t, contextInfo);
281
                CdmErrorDialog ced = new CdmErrorDialog(AbstractUtility.getShell(), title, message, status, stackTraceWithContext, showReason);
282
                ced.open();
283
                Class<? extends Object> clazz = source != null ? source.getClass() : this.getClass();
284

    
285

    
286
                IStatus singleStatus = new Status(IStatus.ERROR,
287
                        status.getPlugin(),
288
                        message,
289
                        new Exception(stackTraceWithContext));
290

    
291
                error(clazz, singleStatus);
292
            }
293
        });
294
    }
295

    
296
    public static void errorDialog(final String title,
297
            final Object source,
298
            final String message,
299
            final String pluginId,
300
            final Throwable t,
301
            boolean addContactMesg) {
302
        errorDialog(title, source, message, pluginId, t, addContactMesg, true);
303

    
304
    }
305

    
306
    /**
307
     * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
308
     */
309
    public static void errorDialog(final String title,
310
            final Object source,
311
            final String message,
312
            final String pluginId,
313
            final Throwable t,
314
            boolean addContactMesg,
315
            boolean showReason) {
316

    
317
        Throwable throwable = t;
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<>();
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

    
409
                String title = null;
410

    
411
                // FIXME cannot access TaxonomicEditorPlugin.PLUGIN_ID from here
412
                // FIXME is there any reason for keeping the lines below?
413
                // String PID = TaxonomicEditorPlugin.PLUGIN_ID;
414
                // String PID = "eu.etaxonomy.taxeditor.application";
415

    
416
                // checking security exceptions for every operation
417
                RuntimeException securityRuntimeException = SecurityExceptionUtils.findSecurityRuntimeException(ex);
418

    
419
                // in case of a security exception it is a warning, else it is an error
420
                if(securityRuntimeException != null){
421
                    title = "Your changes could not be saved!";
422
                    warningDialog(title, source, String.format("You are missing sufficient permissions for the operation \"%s\". %s", operationlabel, hint));
423
                } else {
424
                    title = "Error executing operation";
425
                    errorDialog(title, source, String.format("An error occurred while executing %s. %s", operationlabel, hint), pluginId, ex, true);
426
                }
427
            }
428
        });
429
    }
430

    
431
    /**
432
     * Displays a question {@link org.eclipse.jface.dialogs.MessageDialog}.
433
     *
434
     * @param title
435
     *            a {@link java.lang.String} object.
436
     * @param message
437
     *            a {@link java.lang.String} object.
438
     * @return a boolean.
439
     */
440
    public static boolean confirmDialog(String title, String message) {
441
        return MessageDialog.openQuestion(AbstractUtility.getShell(), title, message);
442
    }
443

    
444
    public static int confirmDialog(String title, String message, String...labels){
445
        MessageDialog dialog =new MessageDialog(AbstractUtility.getShell(), title, null, message, MessageDialog.QUESTION,labels, 0);
446
        int result = dialog.open();
447
        return result;
448
    }
449

    
450
    /**
451
     * Displays a message {@link org.eclipse.jface.dialogs.MessageDialog}.
452
     *
453
     * @param title
454
     * @param source
455
     * @param message
456
     */
457
    public static void messageDialog(final String title, final Object source, final String message) {
458
        MessagingUtils.messageDialog(title, source, message, null, 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
        MessagingUtils.messageDialog(title, source, message, t, true);
480
    }
481

    
482
    /**
483
     * Displays an error {@link org.eclipse.jface.dialogs.MessageDialog}.
484
     *
485
     * @param title
486
     *            The dialogs title
487
     * @param source
488
     *            The object where the warning was generated (used by log4j)
489
     * @param message
490
     *            An informative String to be presented to the user
491
     * @param title
492
     *            The dialogs title
493
     * @param t
494
     *            a Throwable if one exists or null
495
     */
496
    public static void messageDialog(final String title,
497
            final Object source,
498
            final String message,
499
            final Throwable t,
500
            boolean async) {
501
        if(async) {
502
            Display.getDefault().asyncExec(new Runnable() {
503

    
504
                @Override
505
                public void run() {
506
                    MessageDialog.openError(AbstractUtility.getShell(), title, message + getCauseRecursively(t));
507
                    Class<? extends Object> clazz = source != null ? source
508
                            .getClass() : this.getClass();
509
                            error(clazz, message, t);
510
                }
511

    
512

    
513
            });
514
        } else {
515
            MessageDialog.openError(AbstractUtility.getShell(), title, message + getCauseRecursively(t));
516
            Class<? extends Object> clazz = source != null ? source.getClass() : TaxeditorStorePlugin.class;
517
            error(clazz, message, t);
518
        }
519
    }
520

    
521
    public static String getCauseRecursively(Throwable t) {
522
        if(t == null){
523
            return "";
524
        }
525

    
526
        if(t.getCause() != null){
527
            return getCauseRecursively(t.getCause());
528
        }else{
529
            return String.format("\n\nException: %s\nMessage: %s", t.getClass().getSimpleName(), t.getMessage());
530
        }
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
     * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
575
     *
576
     * @param title
577
     *            The dialogs title
578
     * @param source
579
     *            The object where the warning was generated (used by log4j)
580
     * @param message
581
     *            An informative String to be presented to the user
582
     */
583
    public static void warningDialog(final String title, final Object source, final String message) {
584
        Display.getDefault().asyncExec(new Runnable() {
585

    
586
            @Override
587
            public void run() {
588
                MessageDialog.openWarning(AbstractUtility.getShell(), title, message);
589
                Class<? extends Object> clazz = source != null ? source
590
                        .getClass() : AbstractUtility.class;
591
                        warn(clazz, message);
592
            }
593
        });
594
    }
595

    
596
    /**
597
     * Displays an information {@link org.eclipse.jface.dialogs.MessageDialog}.
598
     */
599
    public static void informationDialog(final String title, final IStatus status) {
600
        MessagingUtils.informationDialog(title, status.getMessage());
601
    }
602

    
603
    /**
604
     * Displays an information {@link org.eclipse.jface.dialogs.MessageDialog}.
605
     *
606
     * @param title
607
     *            a {@link java.lang.String} object.
608
     * @param message
609
     *            a {@link java.lang.String} object.
610
     */
611
    public static void informationDialog(final String title,
612
            final String message) {
613
        Display.getDefault().asyncExec(new Runnable() {
614

    
615
            @Override
616
            public void run() {
617
                MessageDialog.openInformation(AbstractUtility.getShell(), title, message);
618
            }
619
        });
620
    }
621

    
622
    /**
623
     * Open a message box that informs the user about unimplemented
624
     * functionality. This method is for developer convenience.
625
     *
626
     * @param source
627
     *            a {@link java.lang.Object} object.
628
     */
629
    public static void notImplementedMessage(Object source) {
630
        warningDialog("Not yet implemented", source,
631
                "This functionality is not yet implemented.");
632
    }
633

    
634
}
(31-31/40)