Project

General

Profile

Download (22.4 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.persistence.hibernate.permission.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
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

    
45
    /**
46
     * Gets the Log4J logger for a given class
47
     *
48
     * @param clazz
49
     *            a {@link java.lang.Class} object.
50
     * @return a {@link org.apache.log4j.Logger} object.
51
     */
52
    public static Logger getLog4JLogger(Class<?> clazz) {
53
        return Logger.getLogger(clazz);
54
    }
55

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

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

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

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

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

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

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

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

    
150
    /**
151
     * Logs an exception from a given source as an error.
152
     *
153
     *
154
     * @param source
155
     *            a {@link java.lang.Class} object.
156
     * @param t
157
     *            a {@link java.lang.Throwable} object.
158
     */
159
    public static void error(Class<?> source, Throwable t) {
160
        error(source.getClass(), t.getMessage(), t);
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 contextPath = "";
176
        String schemaVersion = "";
177
        String server = "";
178
        String version = "";
179
        String login = "";
180
        try {
181
            version = Platform.getBundle("eu.etaxonomy.taxeditor.application").getHeaders().get(org.osgi.framework.Constants.BUNDLE_VERSION);
182

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

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

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

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

    
222
        return contextInfo;
223
    }
224

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

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

    
233
        StringWriter sw = new StringWriter();
234

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

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

    
242
        return stackTraceAndContextInfo.toString();
243
    }
244

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

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

    
252
        return scontextInfoStringBuffer.toString();
253
    }
254

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

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

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

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

    
280

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

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

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

    
299
    }
300

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

    
312
        Throwable throwable = t;
313

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

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

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

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

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

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

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

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

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

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

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

    
401
            @Override
402
            public void run() {
403

    
404
                String title = null;
405

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

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

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

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

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

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

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

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

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

    
507

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

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

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

    
527
    }
528

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

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

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

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

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

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

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

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

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

    
629
}
(32-32/41)