/* * Copyright 2014 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.optaplanner.swing.impl; import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.util.concurrent.Semaphore; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.ScrollPaneConstants; import javax.swing.UIManager; import javax.swing.WindowConstants; // TODO move to optaplanner-swingwb, the Swing version of optaplanner-wb (which doesn't exist yet either) public class SwingUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final Semaphore windowCountSemaphore = new Semaphore(5); public static void register() { SwingUncaughtExceptionHandler exceptionHandler = new SwingUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(exceptionHandler); System.setProperty("sun.awt.exception.handler", SwingUncaughtExceptionHandler.class.getName()); } @Override public void uncaughtException(Thread t, Throwable e) { // Not logger.error() because it needs to show up red (and linked) in the IDE console System.err.append("Exception in thread \"").append(t.getName()).append("\" "); e.printStackTrace(); displayException(t, e); } private void displayException(Thread t, Throwable e) { if (!windowCountSemaphore.tryAcquire()) { System.err.println("Too many exception windows open, failed to display the latest exception."); return; } final JFrame exceptionFrame = new JFrame("Uncaught exception: " + e.getMessage()); Icon errorIcon = UIManager.getIcon("OptionPane.errorIcon"); BufferedImage errorImage = new BufferedImage( errorIcon.getIconWidth(), errorIcon.getIconHeight(), BufferedImage.TYPE_INT_RGB); errorIcon.paintIcon(null, errorImage.getGraphics(), 0, 0); exceptionFrame.setIconImage(errorImage); exceptionFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); JPanel contentPanel = new JPanel(new BorderLayout(5, 5)); contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); contentPanel.add(new JLabel("An uncaught exception has occurred: "), BorderLayout.NORTH); JTextArea stackTraceTextArea = new JTextArea(30, 80); stackTraceTextArea.setEditable(false); stackTraceTextArea.append("Exception in thread \"" + t.getName() + "\" " + e.getClass().getName() + ": " + e.getMessage() + "\n"); for (StackTraceElement stackTraceElement : e.getStackTrace()) { stackTraceTextArea.append(" at " + stackTraceElement.toString() + "\n"); } Throwable parentException = e; Throwable cause = e.getCause(); while (cause != null && cause != parentException) { stackTraceTextArea.append("Caused by: " + "\" " + cause.getClass().getName() + ": " + cause.getMessage() + "\n"); for (StackTraceElement stackTraceElement : e.getStackTrace()) { stackTraceTextArea.append(" at " + stackTraceElement.toString() + "\n"); } parentException = cause; cause = cause.getCause(); } JScrollPane stackTraceScrollPane = new JScrollPane(stackTraceTextArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); contentPanel.add(stackTraceScrollPane, BorderLayout.CENTER); stackTraceTextArea.setCaretPosition(0); // Scroll to top JPanel buttonPanel = new JPanel(new GridLayout(1, 0)); JButton closeButton = new JButton(new AbstractAction("Close") { @Override public void actionPerformed(ActionEvent e) { exceptionFrame.setVisible(false); exceptionFrame.dispose(); } }); buttonPanel.add(closeButton); JButton exitApplicationButton = new JButton(new AbstractAction("Exit application") { @Override public void actionPerformed(ActionEvent e) { System.exit(1); } }); buttonPanel.add(exitApplicationButton); contentPanel.add(buttonPanel, BorderLayout.SOUTH); exceptionFrame.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { windowCountSemaphore.release(); } }); exceptionFrame.setContentPane(contentPanel); exceptionFrame.pack(); exceptionFrame.setLocationRelativeTo(null); exceptionFrame.setVisible(true); } }