OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 132 | Rev 156 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 /*
15
 * ExceptionHandler created on 7 mai 2004
16
 */
17
package org.openconcerto.utils;
18
 
83 ilm 19
import org.openconcerto.utils.SystemInfo.Info;
20
import org.openconcerto.utils.cc.IFactory;
73 ilm 21
import org.openconcerto.utils.io.PercentEncoder;
22
 
17 ilm 23
import java.awt.Component;
24
import java.awt.Desktop;
25
import java.awt.Desktop.Action;
26
import java.awt.Dialog;
27
import java.awt.Dimension;
28
import java.awt.Font;
29
import java.awt.Frame;
30
import java.awt.GraphicsEnvironment;
31
import java.awt.GridBagConstraints;
32
import java.awt.GridBagLayout;
33
import java.awt.Insets;
34
import java.awt.Toolkit;
35
import java.awt.Window;
36
import java.awt.datatransfer.Clipboard;
37
import java.awt.datatransfer.StringSelection;
38
import java.awt.event.ActionEvent;
39
import java.awt.event.ActionListener;
40
import java.awt.event.WindowAdapter;
41
import java.awt.event.WindowEvent;
73 ilm 42
import java.io.BufferedReader;
43
import java.io.InputStreamReader;
44
import java.io.OutputStream;
45
import java.net.HttpURLConnection;
17 ilm 46
import java.net.URI;
73 ilm 47
import java.net.URL;
48
import java.nio.charset.Charset;
83 ilm 49
import java.util.Map;
65 ilm 50
import java.util.concurrent.Future;
51
import java.util.concurrent.FutureTask;
52
import java.util.logging.Level;
17 ilm 53
import java.util.logging.Logger;
73 ilm 54
import java.util.regex.Pattern;
17 ilm 55
 
56
import javax.swing.AbstractAction;
57
import javax.swing.ImageIcon;
58
import javax.swing.JButton;
59
import javax.swing.JDialog;
60
import javax.swing.JLabel;
73 ilm 61
import javax.swing.JOptionPane;
17 ilm 62
import javax.swing.JPanel;
63
import javax.swing.JScrollPane;
64
import javax.swing.JSeparator;
65
import javax.swing.JTextArea;
66
import javax.swing.ScrollPaneConstants;
67
import javax.swing.SwingUtilities;
68
import javax.swing.UIManager;
73 ilm 69
import javax.swing.text.JTextComponent;
17 ilm 70
 
71
/**
72
 * Allow to display an exception both on the GUI and on the console.
73
 *
74
 * @author ILM Informatique 7 mai 2004
75
 */
76
public class ExceptionHandler extends RuntimeException {
77
 
73 ilm 78
    private static final Pattern NL_PATTERN = Pattern.compile("\r?\n");
17 ilm 79
    private static final String ILM_CONTACT = "http://www.ilm-informatique.fr/contact";
144 ilm 80
    private static String forumURL = null;
132 ilm 81
    private static boolean showProbably = false;
144 ilm 82
    private static IFactory<String> softwareInfos = null;
83
    private static ThrowableHandler tHandler = null;
17 ilm 84
 
144 ilm 85
    public static void setThrowableHandler(ThrowableHandler handler) {
86
        tHandler = handler;
87
    }
88
 
17 ilm 89
    public static void setForumURL(String url) {
144 ilm 90
        forumURL = url;
17 ilm 91
    }
92
 
132 ilm 93
    public synchronized static void setShowProbably(boolean showProbably) {
94
        ExceptionHandler.showProbably = showProbably;
95
    }
96
 
97
    public synchronized static boolean isShowProbably() {
98
        return ExceptionHandler.showProbably;
99
    }
100
 
83 ilm 101
    public synchronized static void setSoftwareInformations(final IFactory<String> f) {
144 ilm 102
        softwareInfos = f;
83 ilm 103
    }
104
 
105
    public synchronized static String computeSoftwareInformations() {
144 ilm 106
        if (softwareInfos == null)
83 ilm 107
            return "";
144 ilm 108
        return softwareInfos.createChecked();
83 ilm 109
    }
110
 
17 ilm 111
    static private void copyToClipboard(final String s) {
112
        final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
113
        final StringSelection data = new StringSelection(s);
114
        clipboard.setContents(data, data);
115
    }
116
 
25 ilm 117
    /**
118
     * Display the passed message. Note: this method doesn't block.
119
     *
120
     * @param comp the modal parent of the error window.
121
     * @param msg the message to display.
122
     * @param originalExn the cause, can be <code>null</code>.
123
     * @return an exception.
124
     */
65 ilm 125
    static public ExceptionHandler handle(Component comp, String msg, Throwable originalExn) {
144 ilm 126
        if (tHandler != null && tHandler.handle(msg, originalExn)) {
127
            return new ExceptionHandler(msg, originalExn);
128
        }
17 ilm 129
        return new ExceptionHandler(comp, msg, originalExn, false);
144 ilm 130
 
17 ilm 131
    }
132
 
133
    static public RuntimeException handle(String msg, Throwable originalExn) {
134
        return handle(null, msg, originalExn);
135
    }
136
 
137
    static public RuntimeException handle(String msg) {
138
        return handle(msg, null);
139
    }
140
 
25 ilm 141
    /**
142
     * Display the passed message and quit. Note: this method blocks until the user closes the
143
     * window (then exits).
144
     *
145
     * @param msg the message to display.
146
     * @param originalExn the cause, can be <code>null</code>.
147
     * @return an exception.
148
     */
17 ilm 149
    static public RuntimeException die(String msg, Throwable originalExn) {
150
        return new ExceptionHandler(null, msg, originalExn);
151
    }
152
 
153
    static public RuntimeException die(String msg) {
154
        return die(msg, null);
155
    }
156
 
157
    private static Logger getLogger() {
158
        return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
159
    }
160
 
161
    // the comp on which to display the popup, may be null
162
    private final Component comp;
65 ilm 163
    private final Future<?> future;
17 ilm 164
    private static boolean forceUI;
165
 
166
    public static void setForceUI(boolean forceUI) {
167
        ExceptionHandler.forceUI = forceUI;
168
    }
169
 
65 ilm 170
    private Future<?> display(final boolean error) {
17 ilm 171
        final String msg = this.getMessage();
80 ilm 172
        // write out the message as soon as possible
173
        getLogger().log(error ? Level.SEVERE : Level.INFO, null, this);
174
        // then show it to the user
17 ilm 175
        if (!GraphicsEnvironment.isHeadless() || forceUI) {
176
            if (SwingUtilities.isEventDispatchThread()) {
80 ilm 177
                showMsgHardened(msg, error);
25 ilm 178
            } else {
65 ilm 179
                final FutureTask<?> run = new FutureTask<Object>(new Runnable() {
17 ilm 180
                    public void run() {
80 ilm 181
                        showMsgHardened(msg, error);
17 ilm 182
                    }
65 ilm 183
                }, null);
25 ilm 184
                if (error) {
185
                    try {
186
                        SwingUtilities.invokeAndWait(run);
187
                    } catch (Exception e) {
188
                        e.printStackTrace();
189
                        System.exit(1);
190
                    }
191
                } else {
192
                    SwingUtilities.invokeLater(run);
193
                }
65 ilm 194
                return run;
25 ilm 195
            }
17 ilm 196
        }
65 ilm 197
        return null;
17 ilm 198
    }
199
 
65 ilm 200
    public final Future<?> getDialogFuture() {
201
        return this.future;
202
    }
203
 
80 ilm 204
    protected final void showMsgHardened(final String msg, final boolean error) {
205
        try {
206
            showMsg(msg, error);
207
        } catch (Throwable e) {
208
            // sometimes the VM cannot display the dialog, in that case don't crash the EDT as the
209
            // message has already been logged. Further if this class is used in
210
            // Thread.setDefaultUncaughtExceptionHandler(), it will create an infinite loop.
211
            e = new Exception("Couldn't display message", e);
212
            e.printStackTrace();
213
            try {
214
                // last ditch effort
215
                JOptionPane.showMessageDialog(null, e.getMessage() + " : " + msg);
216
            } catch (Throwable e2) {
144 ilm 217
                // nothing
80 ilm 218
            }
219
        }
220
    }
221
 
17 ilm 222
    protected final void showMsg(final String msg, final boolean quit) {
223
        final JPanel p = new JPanel();
224
        p.setLayout(new GridBagLayout());
225
        final GridBagConstraints c = new GridBagConstraints();
226
        c.insets = new Insets(10, 10, 10, 10);
227
        c.gridx = 0;
228
        c.gridy = 0;
229
        c.fill = GridBagConstraints.BOTH;
80 ilm 230
        final JImage im = new JImage(new ImageIcon(ExceptionHandler.class.getResource("error.png")));
17 ilm 231
        final JLabel l = new JLabel("Une erreur est survenue");
232
        l.setFont(l.getFont().deriveFont(Font.BOLD));
233
 
234
        final JTextArea textArea = new JTextArea();
235
        textArea.setFont(textArea.getFont().deriveFont(11f));
236
 
237
        c.gridheight = 3;
238
        p.add(im, c);
239
        c.insets = new Insets(2, 4, 2, 4);
240
        c.gridheight = 1;
241
        c.gridx++;
242
        c.weightx = 1;
132 ilm 243
        c.gridwidth = 1;
17 ilm 244
        p.add(l, c);
245
        c.gridy++;
73 ilm 246
 
247
        final JLabel lError = new JLabel("<html>" + NL_PATTERN.matcher(msg).replaceAll("<br>") + "</html>");
17 ilm 248
        p.add(lError, c);
73 ilm 249
        c.gridy++;
17 ilm 250
 
132 ilm 251
        if (isShowProbably()) {
252
            p.add(new JLabel("Il s'agit probablement d'une mauvaise configuration ou installation du logiciel."), c);
253
            c.gridy++;
254
        }
17 ilm 255
 
256
        c.gridx = 0;
257
        c.weighty = 0;
132 ilm 258
        c.gridwidth = 2;
17 ilm 259
 
132 ilm 260
        final JPanel btnPanel = new JPanel();
17 ilm 261
        final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
262
        final boolean browseSupported = desktop != null && desktop.isSupported(Action.BROWSE);
144 ilm 263
        if (forumURL != null) {
17 ilm 264
            final javax.swing.Action communityAction;
265
            if (browseSupported) {
266
                communityAction = new AbstractAction("Consulter le forum") {
267
                    @Override
268
                    public void actionPerformed(ActionEvent e) {
73 ilm 269
                        if (desktop != null) {
270
                            try {
144 ilm 271
                                desktop.browse(new URI(forumURL));
73 ilm 272
                            } catch (Exception e1) {
273
                                e1.printStackTrace();
274
                            }
17 ilm 275
                        }
276
                    }
277
                };
278
            } else {
279
                communityAction = new AbstractAction("Copier l'adresse du forum") {
144 ilm 280
 
17 ilm 281
                    @Override
282
                    public void actionPerformed(ActionEvent e) {
144 ilm 283
                        copyToClipboard(forumURL);
17 ilm 284
                    }
285
                };
286
            }
132 ilm 287
            btnPanel.add(new JButton(communityAction));
17 ilm 288
        }
289
 
290
        final javax.swing.Action supportAction;
132 ilm 291
        if (browseSupported) {
17 ilm 292
            supportAction = new AbstractAction("Contacter l'assistance") {
293
                @Override
294
                public void actionPerformed(ActionEvent e) {
73 ilm 295
                    if (desktop != null) {
296
                        try {
297
                            desktop.browse(URI.create(ILM_CONTACT));
298
                        } catch (Exception e1) {
299
                            e1.printStackTrace();
300
                        }
17 ilm 301
                    }
302
 
303
                }
304
            };
132 ilm 305
        } else {
17 ilm 306
            supportAction = new AbstractAction("Copier l'adresse de l'assistance") {
307
                @Override
308
                public void actionPerformed(ActionEvent e) {
309
                    copyToClipboard(ILM_CONTACT);
310
                }
311
            };
132 ilm 312
        }
313
        btnPanel.add(new JButton(supportAction));
17 ilm 314
 
132 ilm 315
        btnPanel.add(new JButton(new AbstractAction("Copier l'erreur") {
144 ilm 316
 
132 ilm 317
            @Override
318
            public void actionPerformed(ActionEvent e) {
319
                copyToClipboard(textArea.getText());
320
            }
321
        }));
73 ilm 322
 
323
        final javax.swing.Action submitAction = new AbstractAction("Soumettre l'erreur") {
324
            @Override
325
            public void actionPerformed(ActionEvent e) {
326
                submitError(p, textArea);
327
            }
328
 
329
            private void submitError(final JPanel p, final JTextComponent textArea) {
330
                final Charset cs = StringUtils.UTF8;
331
                try {
332
                    ProductInfo productInfo = ProductInfo.getInstance();
333
 
334
                    String name = "", version = "";
335
                    if (productInfo != null) {
336
                        name = productInfo.getName();
337
                        version = productInfo.getProperty(ProductInfo.VERSION, version);
338
                    }
83 ilm 339
 
340
                    final Map<Info, String> systemInfos = SystemInfo.get(false);
341
                    final String os = systemInfos.remove(Info.OS);
342
                    final String java = systemInfos.toString();
132 ilm 343
                    final String encodedData = "java=" + PercentEncoder.encode(java, cs) + "&os=" + PercentEncoder.encode(os, cs) + "&software=" + PercentEncoder.encode(name + version, cs) + "&stack="
344
                            + PercentEncoder.encode(computeSoftwareInformations() + "\n\n" + textArea.getText(), cs);
73 ilm 345
                    final String request = "http://bugreport.ilm-informatique.fr:5000/bugreport";
346
                    final URL url = new URL(request);
347
                    final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
348
                    connection.setDoOutput(true);
349
                    connection.setRequestMethod("POST");
350
                    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
351
                    connection.setRequestProperty("charset", cs.name());
352
                    final byte[] bytes = encodedData.getBytes(cs);
353
                    connection.setRequestProperty("Content-Length", String.valueOf(bytes.length));
354
 
355
                    final OutputStream outputStream = connection.getOutputStream();
356
                    outputStream.write(bytes);
357
                    outputStream.flush();
358
 
359
                    // Get the response
144 ilm 360
                    final StringBuilder answer = new StringBuilder();
73 ilm 361
                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
362
                    String line;
363
                    while ((line = reader.readLine()) != null) {
364
                        answer.append(line);
365
                    }
366
                    outputStream.close();
367
                    reader.close();
368
                    connection.disconnect();
369
 
370
                    JOptionPane.showMessageDialog(p, "Merci d'avoir envoyé le rapport d'erreur au service technique.\nIl sera analysé prochainement.");
371
                } catch (Exception ex) {
372
                    ex.printStackTrace();
373
                }
374
            }
375
        };
376
 
132 ilm 377
        btnPanel.add(new JButton(submitAction));
73 ilm 378
 
132 ilm 379
        c.fill = GridBagConstraints.NONE;
380
        c.anchor = GridBagConstraints.EAST;
381
        p.add(btnPanel, c);
382
 
17 ilm 383
        c.gridy++;
384
        c.gridx = 0;
132 ilm 385
        c.gridwidth = 2;
17 ilm 386
        c.fill = GridBagConstraints.BOTH;
387
        c.insets = new Insets(0, 0, 0, 0);
388
        p.add(new JSeparator(), c);
389
 
390
        c.gridx = 0;
391
        c.gridy++;
392
        c.insets = new Insets(2, 4, 2, 4);
393
        p.add(new JLabel("Détails de l'erreur:"), c);
394
        c.insets = new Insets(0, 0, 0, 0);
395
        c.gridy++;
396
        String message = this.getCause() == null ? null : this.getCause().getMessage();
397
        if (message == null) {
398
            message = msg;
399
        } else {
400
            message = msg + "\n\n" + message;
401
        }
402
        message += "\n";
144 ilm 403
        message +=
404
 
405
                getTrace();
17 ilm 406
        textArea.setText(message);
407
        textArea.setEditable(false);
144 ilm 408
 
17 ilm 409
        // Scroll
410
        JScrollPane scroll = new JScrollPane(textArea);
411
        scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
412
        scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
413
        scroll.getViewport().setMinimumSize(new Dimension(200, 300));
414
        c.weighty = 1;
415
        c.gridx = 0;
416
        c.gridy++;
417
        p.add(scroll, c);
418
 
419
        c.gridy++;
420
        c.fill = GridBagConstraints.NONE;
421
        c.weighty = 0;
422
        c.insets = new Insets(2, 4, 2, 4);
423
        final JButton buttonClose = new JButton("Fermer");
424
        p.add(buttonClose, c);
425
 
426
        final Window window = this.comp == null ? null : SwingUtilities.getWindowAncestor(this.comp);
427
        final JDialog f;
428
        if (window instanceof Frame) {
429
            f = new JDialog((Frame) window, "Erreur", true);
430
        } else {
431
            f = new JDialog((Dialog) window, "Erreur", true);
432
        }
433
        f.setContentPane(p);
434
        f.pack();
435
        f.setSize(580, 680);
436
        f.setMinimumSize(new Dimension(380, 380));
437
        f.setLocationRelativeTo(this.comp);
438
        final ActionListener al = new ActionListener() {
439
 
440
            @Override
441
            public void actionPerformed(ActionEvent e) {
442
                if (quit) {
443
                    System.exit(1);
444
                } else {
445
                    f.dispose();
446
                }
447
 
448
            }
449
        };
450
        buttonClose.addActionListener(al);
451
        // cannot set EXIT_ON_CLOSE on JDialog
452
        f.addWindowListener(new WindowAdapter() {
453
            @Override
454
            public void windowClosing(WindowEvent e) {
455
                al.actionPerformed(null);
456
            }
457
        });
90 ilm 458
 
459
        f.setVisible(true);
17 ilm 460
    }
461
 
462
    private String getTrace() {
463
        return ExceptionUtils.getStackTrace(this);
464
    }
465
 
466
    /**
467
     * Affiche l'erreur et quitte.
468
     *
469
     * @param comp the component upon which to display the popup.
470
     * @param msg le message d'erreur à afficher.
471
     * @param cause la cause de l'exception (peut être <code>null</code>).
472
     */
473
    private ExceptionHandler(Component comp, String msg, Throwable cause) {
474
        this(comp, msg, cause, true);
475
    }
476
 
477
    /**
478
     * Affiche l'erreur et quitte suivant l'option passée.
479
     *
480
     * @param comp the component upon which to display the popup.
481
     * @param msg the error message to display.
482
     * @param cause the cause of the exception (maybe <code>null</code>).
483
     * @param quit if the VM must exit.
484
     */
485
    private ExceptionHandler(Component comp, String msg, Throwable cause, boolean quit) {
486
        super(msg, cause);
487
        this.comp = comp;
65 ilm 488
        this.future = this.display(quit);
17 ilm 489
    }
490
 
144 ilm 491
    public ExceptionHandler(String msg, Throwable cause) {
492
        super(msg, cause);
493
        this.comp = null;
494
        this.future = null;
495
    }
496
 
17 ilm 497
    public static void main(String[] args) throws Exception {
498
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
73 ilm 499
        ExceptionHandler.handle("Fichier de configuration corrompu\n\nmulti\nline", new IllegalStateException("Id manquant"));
17 ilm 500
    }
501
}