OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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