Project

General

Profile

Download (22.7 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;import org.apache.logging.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.persistence.permission.SecurityExceptionUtils;
22
import eu.etaxonomy.taxeditor.remoting.RemoteExecutionTimestampsUtil;
23
import eu.etaxonomy.taxeditor.remoting.source.ICdmRemoteSource;
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
public class MessagingUtils {
36

    
37
    public final static String UNEXPECTED_ERROR_MESSAGE = "An error occurred.";
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
    public static final String PERMISSION_DENIED = "You do not have the permission to ";
45
    public static final String RESTART_EDITOR_MESSAGE = "An error occured that may require restarting the editor.";
46
    /**
47
     * Gets the Log4J logger for a given class
48
     *
49
     * @param clazz
50
     *            a {@link java.lang.Class} object.
51
     * @return a {@link org.apache.logging.log4j.Logger} object.
52
     */
53
    public static Logger getLog4JLogger(Class<?> clazz) {
54
        return LogManager.getLogger(clazz);
55
    }
56

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

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

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

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

    
97
    /**
98
     * Logs a status object from a given source as a warning.
99
     */
100
    public static void warn(Class<?> source, IStatus status) {
101
        MessagingUtils.getLog4JLogger(source).warn(status.getMessage(), status.getException());
102
        log(status);
103
    }
104

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

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

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

    
151
    /**
152
     * Logs an exception from a given source as an error.
153
     *
154
     *
155
     * @param source
156
     *            a {@link java.lang.Class} object.
157
     * @param t
158
     *            a {@link java.lang.Throwable} object.
159
     */
160
    public static void error(Class<?> source, Throwable t) {
161
        error(source.getClass(), t.getMessage(), t);
162
    }
163

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

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

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

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

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

    
223
        return contextInfo;
224
    }
225

    
226
    public static String getStackTraceAndContextInfo(Throwable t, List<String> contextInfo)  {
227
        StringBuffer stackTraceAndContextInfo = new StringBuffer();
228
        Throwable throwable = t;
229

    
230
        for(String infoItem : contextInfo) {
231
            stackTraceAndContextInfo.append(infoItem + System.getProperty("line.separator"));
232
        }
233

    
234
        StringWriter sw = new StringWriter();
235

    
236
        if(throwable == null) {
237
            throwable = getDefaultThrowable();
238
        }
239
        throwable.printStackTrace(new PrintWriter(sw));
240

    
241
        stackTraceAndContextInfo.append(sw.toString());
242

    
243
        return stackTraceAndContextInfo.toString();
244
    }
245

    
246
    public static String getContextInfo(List<String> contextInfo)  {
247
        StringBuffer scontextInfoStringBuffer = new StringBuffer();
248

    
249
        for(String infoItem : contextInfo) {
250
            scontextInfoStringBuffer.append(infoItem + System.getProperty("line.separator"));
251
        }
252

    
253
        return scontextInfoStringBuffer.toString();
254
    }
255

    
256
    private static Throwable getDefaultThrowable() {
257
        return new Throwable("Error thrown but no associated exception");
258
    }
259

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

    
272
        Display.getDefault().asyncExec(new Runnable() {
273

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

    
281

    
282
                IStatus singleStatus = new Status(IStatus.ERROR,
283
                        status.getPlugin(),
284
                        message,
285
                        new Exception(stackTraceWithContext));
286

    
287
                error(clazz, singleStatus);
288
            }
289
        });
290
    }
291

    
292
    public static void errorDialog(final String title,
293
            final Object source,
294
            final String message,
295
            final String pluginId,
296
            final Throwable t,
297
            boolean addContactMesg) {
298
        errorDialog(title, source, message, pluginId, t, addContactMesg, true);
299

    
300
    }
301

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

    
313
        Throwable throwable = t;
314

    
315
        // We need to build a MultiStatus object since the simple
316
        // idea of writing out the stack trace as a single string
317
        // leads to a single line on windows
318
        List<Status> childStatuses = new ArrayList<>();
319

    
320
        // add context info
321
        List<String> contextInfo = getContextInfo();
322
        for(String infoItem : contextInfo) {
323
            childStatuses.add(new Status(IStatus.ERROR, pluginId, infoItem));
324
        }
325

    
326
        if(throwable == null) {
327
            throwable = getDefaultThrowable();
328
        }
329

    
330
        int thCount = 0;
331
        int maxTraces = 4;
332

    
333
        for(Throwable th : ExceptionUtils.getThrowables(throwable)) {
334
            // add main exception
335
            if(thCount == 0) {
336
                for (StackTraceElement ste : th.getStackTrace()) {
337
                    childStatuses.add(new Status(IStatus.ERROR, pluginId, "  at " + ste.toString()));
338
                }
339
            } else {
340
                // add recursive causes
341
                if(th != null) {
342
                    childStatuses.add(new Status(IStatus.ERROR, pluginId, ""));
343
                    String msg = th.toString();
344
                    childStatuses.add(new Status(IStatus.ERROR, pluginId, "Caused by : " + msg));
345
                    int traceCount = 0;
346
                    for (StackTraceElement ste : th.getStackTrace()) {
347
                        // add only pre-defined number of trace elements
348
                        if(traceCount > maxTraces) {
349
                            childStatuses.add(new Status(IStatus.ERROR, pluginId, "  ...."));
350
                            break;
351
                        }
352
                        // build & add status
353
                        childStatuses.add(new Status(IStatus.ERROR, pluginId, "  at " + ste.toString()));
354
                        traceCount++;
355
                    }
356
                }
357
            }
358
            thCount++;
359
        }
360
        String finalMessage = message;
361

    
362
        if(finalMessage == null || finalMessage.isEmpty()) {
363
            finalMessage = DEFAULT_MESSAGE;
364
        }
365

    
366
        if(addContactMesg) {
367
            // add edit support contact info to message
368
            finalMessage += MessagingUtils.CONTACT_MESSAGE;
369
        }
370

    
371
        MultiStatus ms = new MultiStatus(pluginId,
372
                IStatus.ERROR,
373
                childStatuses.toArray(new Status[] {}),
374
                throwable.toString(),
375
                throwable);
376

    
377
        errorDialog(title, source, throwable, contextInfo, finalMessage, ms, showReason);
378
    }
379

    
380
    /**
381
     * Displays a dialog for an exception occurring in an operation.
382
     *
383
     * This will be either a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog} in case of a
384
     * security runtime exception or a warning {@link org.eclipse.jface.dialogs.MessageDialog} in
385
     * case of any other exception.
386
     *
387
     * @param title
388
     *            a {@link java.lang.String} object.
389
     * @param source
390
     *            a {@link java.lang.Object} object.
391
     * @param status
392
     *            a {@link org.eclipse.core.runtime.IStatus} object.
393
     */
394
    public static void operationDialog(final Object source,
395
            final Exception ex,
396
            final String pluginId,
397
            final String operationlabel,
398
            final String hint) {
399

    
400
        Display.getDefault().asyncExec(new Runnable() {
401

    
402
            @Override
403
            public void run() {
404

    
405
                String title = null;
406

    
407
                // FIXME cannot access TaxonomicEditorPlugin.PLUGIN_ID from here
408
                // FIXME is there any reason for keeping the lines below?
409
                // String PID = TaxonomicEditorPlugin.PLUGIN_ID;
410
                // String PID = "eu.etaxonomy.taxeditor.application";
411

    
412
                // checking security exceptions for every operation
413
                RuntimeException securityRuntimeException = SecurityExceptionUtils.findSecurityRuntimeException(ex);
414

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

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

    
440
    public static int confirmDialog(String title, String message, String...labels){
441
        MessageDialog dialog =new MessageDialog(AbstractUtility.getShell(), title, null, message, MessageDialog.QUESTION,labels, 0);
442
        int result = dialog.open();
443
        return result;
444
    }
445

    
446
    /**
447
     * Displays a message {@link org.eclipse.jface.dialogs.MessageDialog}.
448
     *
449
     * @param title
450
     * @param source
451
     * @param message
452
     */
453
    public static void messageDialog(final String title, final Object source, final String message) {
454
        MessagingUtils.messageDialog(title, source, message, null, true);
455
    }
456

    
457
    /**
458
     * Displays an error {@link org.eclipse.jface.dialogs.MessageDialog}.
459
     *
460
     * @param title
461
     *            The dialogs title
462
     * @param source
463
     *            The object where the warning was generated (used by log4j)
464
     * @param message
465
     *            An informative String to be presented to the user
466
     * @param title
467
     *            The dialogs title
468
     * @param t
469
     *            a Throwable if one exists or null
470
     */
471
    public static void messageDialog(final String title,
472
            final Object source,
473
            final String message,
474
            final Throwable t) {
475
        MessagingUtils.messageDialog(title, source, message, t, true);
476
    }
477

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

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

    
508

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

    
517
    public static String getCauseRecursively(Throwable t) {
518
        if(t == null){
519
            return "";
520
        }
521

    
522
        if(t.getCause() != null){
523
            return getCauseRecursively(t.getCause());
524
        }else{
525
            return String.format("\n\nException: %s\nMessage: %s", t.getClass().getSimpleName(), t.getMessage());
526
        }
527

    
528
    }
529

    
530
    /**
531
     * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
532
     *
533
     * @param title
534
     * @param termBase
535
     * @param status
536
     */
537
    public static void warningDialog(String title, Object source,
538
            IStatus status) {
539
        MessagingUtils.warningDialog(title, source, status.getMessage());
540
    }
541

    
542
    /**
543
     * Standard warning dialog for the case when the application is not yet connected to the datasource.
544
     *
545
     * @param source
546
     */
547
    public static void noDataSourceWarningDialog(Object source) {
548
        MessagingUtils
549
        .warningDialog(
550
                "Application is not connected to a datastore",
551
                source,
552
                "The requested operation is only available when "
553
                + "connected to a datasource. You may choose a datasource to connect to or create a new one in the datasource view.");
554
    }
555

    
556
    /**
557
     * Standard warning dialog for the case when the datasource is not available
558
     *
559
     * @param source
560
     */
561
    public static void dataSourceNotAvailableWarningDialog(Object source) {
562
        MessagingUtils
563
        .warningDialog(
564
                "The datasource is not available",
565
                source,
566
                "The editor is not connected to a datasource. Maybe the datasource is not available.");
567
    }
568

    
569
    /**
570
     * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
571
     *
572
     * @param title
573
     *            The dialogs title
574
     * @param source
575
     *            The object where the warning was generated (used by log4j)
576
     * @param message
577
     *            An informative String to be presented to the user
578
     */
579
    public static void warningDialog(final String title, final Object source, final String message) {
580
        Display.getDefault().asyncExec(new Runnable() {
581

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

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

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

    
611
            @Override
612
            public void run() {
613
                MessageDialog.openInformation(AbstractUtility.getShell(), title, message);
614
            }
615
        });
616
    }
617

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

    
630
}
(32-32/41)