OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 65 | Rev 80 | 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();
141
        if (error) {
65 ilm 142
            getLogger().log(Level.SEVERE, null, this);
17 ilm 143
        } else {
144
            if (this.getCause() != null) {
65 ilm 145
                getLogger().log(Level.INFO, null, this);
17 ilm 146
            }
147
        }
148
        if (!GraphicsEnvironment.isHeadless() || forceUI) {
149
            if (SwingUtilities.isEventDispatchThread()) {
150
                showMsg(msg, error);
25 ilm 151
            } else {
65 ilm 152
                final FutureTask<?> run = new FutureTask<Object>(new Runnable() {
17 ilm 153
                    public void run() {
154
                        showMsg(msg, error);
155
                    }
65 ilm 156
                }, null);
25 ilm 157
                if (error) {
158
                    try {
159
                        SwingUtilities.invokeAndWait(run);
160
                    } catch (Exception e) {
161
                        e.printStackTrace();
162
                        System.exit(1);
163
                    }
164
                } else {
165
                    SwingUtilities.invokeLater(run);
166
                }
65 ilm 167
                return run;
25 ilm 168
            }
17 ilm 169
        }
65 ilm 170
        return null;
17 ilm 171
    }
172
 
65 ilm 173
    public final Future<?> getDialogFuture() {
174
        return this.future;
175
    }
176
 
17 ilm 177
    protected final void showMsg(final String msg, final boolean quit) {
178
        final JPanel p = new JPanel();
179
        p.setLayout(new GridBagLayout());
180
        final GridBagConstraints c = new GridBagConstraints();
181
        c.insets = new Insets(10, 10, 10, 10);
182
        c.gridx = 0;
183
        c.gridy = 0;
184
        c.fill = GridBagConstraints.BOTH;
185
        final JImage im = new JImage(new ImageIcon(this.getClass().getResource("error.png")));
186
        final JLabel l = new JLabel("Une erreur est survenue");
187
        l.setFont(l.getFont().deriveFont(Font.BOLD));
188
 
189
        final JTextArea textArea = new JTextArea();
190
        textArea.setFont(textArea.getFont().deriveFont(11f));
191
 
192
        c.gridheight = 3;
193
        p.add(im, c);
194
        c.insets = new Insets(2, 4, 2, 4);
195
        c.gridheight = 1;
196
        c.gridx++;
197
        c.weightx = 1;
198
        c.gridwidth = 2;
199
        p.add(l, c);
200
        c.gridy++;
73 ilm 201
 
202
        final JLabel lError = new JLabel("<html>" + NL_PATTERN.matcher(msg).replaceAll("<br>") + "</html>");
17 ilm 203
        p.add(lError, c);
73 ilm 204
        c.gridy++;
17 ilm 205
 
206
        p.add(new JLabel("Il s'agit probablement d'une mauvaise configuration ou installation du logiciel."), c);
207
 
208
        c.gridx = 0;
73 ilm 209
        c.gridwidth = 4;
17 ilm 210
        c.gridy++;
211
        c.weighty = 0;
212
        c.gridwidth = 1;
213
        c.gridx = 1;
214
        c.gridy++;
215
 
216
        c.fill = GridBagConstraints.NONE;
217
        c.anchor = GridBagConstraints.EAST;
218
        final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
219
        final boolean browseSupported = desktop != null && desktop.isSupported(Action.BROWSE);
220
        if (ForumURL != null) {
221
            final javax.swing.Action communityAction;
222
            if (browseSupported) {
223
                communityAction = new AbstractAction("Consulter le forum") {
224
                    @Override
225
                    public void actionPerformed(ActionEvent e) {
73 ilm 226
                        if (desktop != null) {
227
                            try {
228
                                desktop.browse(new URI(ForumURL));
229
                            } catch (Exception e1) {
230
                                e1.printStackTrace();
231
                            }
17 ilm 232
                        }
233
                    }
234
                };
235
            } else {
236
                communityAction = new AbstractAction("Copier l'adresse du forum") {
237
                    @Override
238
                    public void actionPerformed(ActionEvent e) {
239
                        copyToClipboard(ForumURL);
240
                    }
241
                };
242
            }
243
            p.add(new JButton(communityAction), c);
244
        }
245
        c.weightx = 0;
246
        c.gridx++;
247
 
248
        final javax.swing.Action supportAction;
249
        if (browseSupported)
250
            supportAction = new AbstractAction("Contacter l'assistance") {
251
                @Override
252
                public void actionPerformed(ActionEvent e) {
73 ilm 253
                    if (desktop != null) {
254
                        try {
255
                            desktop.browse(URI.create(ILM_CONTACT));
256
                        } catch (Exception e1) {
257
                            e1.printStackTrace();
258
                        }
17 ilm 259
                    }
260
 
261
                }
262
            };
263
        else
264
            supportAction = new AbstractAction("Copier l'adresse de l'assistance") {
265
                @Override
266
                public void actionPerformed(ActionEvent e) {
267
                    copyToClipboard(ILM_CONTACT);
268
                }
269
            };
270
 
271
        p.add(new JButton(supportAction), c);
73 ilm 272
 
273
        c.gridx++;
274
 
275
        final javax.swing.Action submitAction = new AbstractAction("Soumettre l'erreur") {
276
            @Override
277
            public void actionPerformed(ActionEvent e) {
278
                submitError(p, textArea);
279
            }
280
 
281
            private void submitError(final JPanel p, final JTextComponent textArea) {
282
                final Charset cs = StringUtils.UTF8;
283
                try {
284
                    ProductInfo productInfo = ProductInfo.getInstance();
285
 
286
                    String name = "", version = "";
287
                    if (productInfo != null) {
288
                        name = productInfo.getName();
289
                        version = productInfo.getProperty(ProductInfo.VERSION, version);
290
                    }
291
                    final String java = getProperty("java.runtime.version") != null ? getProperty("java.runtime.version") : getProperty("java.version");
292
                    final String os = getProperty("os.name") + " " + getProperty("os.version") + " (" + getProperty("os.arch") + ")";
293
                    final String encodedData = "java=" + PercentEncoder.encode(java, cs) + "&os=" + PercentEncoder.encode(os, cs) + "&software=" + PercentEncoder.encode(name + version, cs)
294
                            + "&stack=" + PercentEncoder.encode(textArea.getText(), cs);
295
                    final String request = "http://bugreport.ilm-informatique.fr:5000/bugreport";
296
                    final URL url = new URL(request);
297
                    final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
298
                    connection.setDoOutput(true);
299
                    connection.setRequestMethod("POST");
300
                    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
301
                    connection.setRequestProperty("charset", cs.name());
302
                    final byte[] bytes = encodedData.getBytes(cs);
303
                    connection.setRequestProperty("Content-Length", String.valueOf(bytes.length));
304
 
305
                    final OutputStream outputStream = connection.getOutputStream();
306
                    outputStream.write(bytes);
307
                    outputStream.flush();
308
 
309
                    // Get the response
310
                    final StringBuffer answer = new StringBuffer();
311
                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
312
                    String line;
313
                    while ((line = reader.readLine()) != null) {
314
                        answer.append(line);
315
                    }
316
                    outputStream.close();
317
                    reader.close();
318
                    connection.disconnect();
319
 
320
                    JOptionPane.showMessageDialog(p, "Merci d'avoir envoyé le rapport d'erreur au service technique.\nIl sera analysé prochainement.");
321
                } catch (Exception ex) {
322
                    ex.printStackTrace();
323
                }
324
            }
325
        };
326
 
327
        p.add(new JButton(submitAction), c);
328
 
17 ilm 329
        c.gridy++;
330
        c.gridx = 0;
73 ilm 331
        c.gridwidth = 4;
17 ilm 332
        c.fill = GridBagConstraints.BOTH;
333
        c.insets = new Insets(0, 0, 0, 0);
334
        p.add(new JSeparator(), c);
335
 
336
        c.gridx = 0;
337
        c.gridwidth = 3;
338
        c.gridy++;
339
        c.insets = new Insets(2, 4, 2, 4);
340
        p.add(new JLabel("Détails de l'erreur:"), c);
341
        c.insets = new Insets(0, 0, 0, 0);
342
        c.gridy++;
343
        String message = this.getCause() == null ? null : this.getCause().getMessage();
344
        if (message == null) {
345
            message = msg;
346
        } else {
347
            message = msg + "\n\n" + message;
348
        }
349
        message += "\n";
350
        message += getTrace();
351
        textArea.setText(message);
352
        textArea.setEditable(false);
353
        // Scroll
354
        JScrollPane scroll = new JScrollPane(textArea);
355
        scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
356
        scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
357
        scroll.getViewport().setMinimumSize(new Dimension(200, 300));
358
        c.weighty = 1;
73 ilm 359
        c.gridwidth = 4;
17 ilm 360
        c.gridx = 0;
361
        c.gridy++;
362
        p.add(scroll, c);
363
 
364
        c.gridy++;
365
        c.fill = GridBagConstraints.NONE;
366
        c.weighty = 0;
367
        c.insets = new Insets(2, 4, 2, 4);
368
        final JButton buttonClose = new JButton("Fermer");
369
        p.add(buttonClose, c);
370
 
371
        final Window window = this.comp == null ? null : SwingUtilities.getWindowAncestor(this.comp);
372
        final JDialog f;
373
        if (window instanceof Frame) {
374
            f = new JDialog((Frame) window, "Erreur", true);
375
        } else {
376
            f = new JDialog((Dialog) window, "Erreur", true);
377
        }
378
        f.setContentPane(p);
379
        f.pack();
380
        f.setSize(580, 680);
381
        f.setMinimumSize(new Dimension(380, 380));
382
        f.setLocationRelativeTo(this.comp);
383
        final ActionListener al = new ActionListener() {
384
 
385
            @Override
386
            public void actionPerformed(ActionEvent e) {
387
                if (quit) {
388
                    System.exit(1);
389
                } else {
390
                    f.dispose();
391
                }
392
 
393
            }
394
        };
395
        buttonClose.addActionListener(al);
396
        // cannot set EXIT_ON_CLOSE on JDialog
397
        f.addWindowListener(new WindowAdapter() {
398
            @Override
399
            public void windowClosing(WindowEvent e) {
400
                al.actionPerformed(null);
401
            }
402
        });
403
        f.setVisible(true);
404
    }
405
 
406
    private String getTrace() {
407
        return ExceptionUtils.getStackTrace(this);
408
    }
409
 
410
    /**
411
     * Affiche l'erreur et quitte.
412
     *
413
     * @param comp the component upon which to display the popup.
414
     * @param msg le message d'erreur à afficher.
415
     * @param cause la cause de l'exception (peut être <code>null</code>).
416
     */
417
    private ExceptionHandler(Component comp, String msg, Throwable cause) {
418
        this(comp, msg, cause, true);
419
    }
420
 
421
    /**
422
     * Affiche l'erreur et quitte suivant l'option passée.
423
     *
424
     * @param comp the component upon which to display the popup.
425
     * @param msg the error message to display.
426
     * @param cause the cause of the exception (maybe <code>null</code>).
427
     * @param quit if the VM must exit.
428
     */
429
    private ExceptionHandler(Component comp, String msg, Throwable cause, boolean quit) {
430
        super(msg, cause);
431
        this.comp = comp;
65 ilm 432
        this.future = this.display(quit);
17 ilm 433
    }
434
 
435
    public static void main(String[] args) throws Exception {
436
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
73 ilm 437
        ExceptionHandler.handle("Fichier de configuration corrompu\n\nmulti\nline", new IllegalStateException("Id manquant"));
17 ilm 438
    }
439
}