Java tutorial
/** * Thread Dump Analysis Tool, parses Thread Dump input and displays it as tree * <p> * This file is part of TDA - Thread Dump Analysis Tool. * <p> * TDA is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * <p> * TDA is distributed in the hope that it will be useful,h * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Lesser GNU General Public License for more details. * <p> * TDA should have received a copy of the Lesser GNU General Public License * along with Foobar; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * <p> * $Id: TDA.java,v 1.190 2010-02-03 12:40:29 irockel Exp $ */ package com.pironet.tda; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Image; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetAdapter; import java.awt.dnd.DropTargetDropEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.URL; import java.text.NumberFormat; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.ProgressMonitorInputStream; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.HyperlinkEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.filechooser.FileFilter; import javax.swing.plaf.FontUIResource; import javax.swing.text.Position; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import com.google.common.base.Strings; import com.pironet.tda.jconsole.MBeanDumper; import com.pironet.tda.utils.AppInfo; import com.pironet.tda.utils.Browser; import com.pironet.tda.utils.Const; import com.pironet.tda.utils.HistogramTableModel; import com.pironet.tda.utils.MonitorComparator; import com.pironet.tda.utils.PrefManager; import com.pironet.tda.utils.ResourceManager; import com.pironet.tda.utils.StatusBar; import com.pironet.tda.utils.SwingWorker; import com.pironet.tda.utils.TableSorter; import com.pironet.tda.utils.ThreadsTableModel; import com.pironet.tda.utils.ThreadsTableSelectionModel; import com.pironet.tda.utils.TipOfDay; import com.pironet.tda.utils.TreeRenderer; import com.pironet.tda.utils.ViewScrollPane; import com.pironet.tda.utils.jedit.JEditTextArea; import com.pironet.tda.utils.jedit.PopupMenu; import org.apache.commons.io.IOUtils; /** * main class of the Thread Dump Analyzer. Start using static main method. * * @author irockel */ public class TDA extends JPanel implements ListSelectionListener, TreeSelectionListener, ActionListener, MenuListener { private static final long serialVersionUID = -5752755762424187064L; private static final Pattern PATTERN_B_SLASHES = Pattern.compile("\\\\"); private static JFileChooser fc; private static JFileChooser sessionFc; protected static JFrame frame; /** * sync object is needed to synchronize opening of multiple files. */ private static final Object syncObject = new Object(); private static String dumpFile; private static int fontSizeModifier = 0; private static TDA myTDA = null; private JEditorPane htmlPane; private JEditTextArea jeditPane; protected JTree tree; protected DefaultTreeModel treeModel; private JSplitPane splitPane; protected JSplitPane topSplitPane; private DumpStore dumpStore; private List<DefaultMutableTreeNode> topNodes; private ViewScrollPane htmlView; private JTextField filter; private JCheckBox checkCase; private PreferencesDialog prefsDialog; private FilterDialog filterDialog; private CustomCategoriesDialog categoriesDialog; private JTable histogramTable; private JMenuItem showDumpMenuItem; boolean runningAsJConsolePlugin; boolean runningAsVisualVMPlugin; private DefaultMutableTreeNode logFile; private MBeanDumper mBeanDumper; private MainMenu pluginMainMenu; private boolean isFoundClassHistogram = false; private DropTarget dt = null; private DropTarget hdt = null; private int dumpCounter; private static final Font SANS_SERIF = new Font("SansSerif", Font.PLAIN, Const.FONT_SIZE); private StatusBar statusBar; private SearchDialog searchDialog; /** * singleton access method for TDA */ public static TDA get(boolean setLF) { if (myTDA == null) { myTDA = new TDA(setLF); } return (myTDA); } /** * constructor (needs to be public for plugin) */ public TDA(boolean setLF) { super(new BorderLayout()); if (setLF) { // init L&F setupLookAndFeel(); } } /** * constructor (needs to be public for plugin) */ public TDA(boolean setLF, MBeanDumper mBeanDumper) { this(setLF); this.mBeanDumper = mBeanDumper; } public TDA(boolean setLF, String dumpFile) { this(setLF); TDA.dumpFile = dumpFile; } /** * initializes tda panel * * @param asJConsolePlugin specifies if tda is running as plugin */ public void init(boolean asJConsolePlugin, boolean asVisualVMPlugin) { // init everything tree = new JTree(); addTreeListener(tree); runningAsJConsolePlugin = asJConsolePlugin; runningAsVisualVMPlugin = asVisualVMPlugin; //Create the HTML viewing pane. if (!this.runningAsVisualVMPlugin && !this.runningAsJConsolePlugin) { InputStream is = TDA.class.getResourceAsStream("/doc/welcome.html"); htmlPane = new JEditorPane(); String welcomeText = parseWelcomeURL(is); htmlPane.setContentType("text/html"); htmlPane.setText(welcomeText); } else if (asJConsolePlugin) { htmlPane = new JEditorPane("text/html", "<html><body bgcolor=\"ffffff\"><i>Press Button above to request a thread dump.</i></body></html>"); } else { htmlPane = new JEditorPane("text/html", "<html><body bgcolor=\"ffffff\"></body></html>"); } htmlPane.putClientProperty(Const.AA_TEXT_INFO_PROPERTY_KEY, Boolean.TRUE); htmlPane.setEditable(false); if (!asJConsolePlugin && !asVisualVMPlugin) { hdt = new DropTarget(htmlPane, new FileDropTargetListener()); } JEditorPane emptyPane = new JEditorPane("text/html", ""); emptyPane.setEditable(false); htmlPane.addHyperlinkListener(evt -> { // if a link was clicked if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { if (evt.getDescription().startsWith("monitor")) { navigateToMonitor(evt.getDescription()); } else if (evt.getDescription().startsWith("dump")) { navigateToDump(); } else if (evt.getDescription().startsWith("wait")) { navigateToChild("Threads waiting"); } else if (evt.getDescription().startsWith("sleep")) { navigateToChild("Threads sleeping"); } else if (evt.getDescription().startsWith("dead")) { navigateToChild("Deadlocks"); } else if (evt.getDescription().startsWith("threaddump")) { addMXBeanDump(); } else if (evt.getDescription().startsWith("openlogfile") && !evt.getDescription().endsWith("//")) { File[] files = { new File(evt.getDescription().substring(14)) }; openFiles(files, false); } else if (evt.getDescription().startsWith("openlogfile")) { chooseFile(); } else if (evt.getDescription().startsWith("opensession") && !evt.getDescription().endsWith("//")) { File file = new File(evt.getDescription().substring(14)); openSession(file, true); } else if (evt.getDescription().startsWith("opensession")) { openSession(); } else if (evt.getDescription().startsWith("preferences")) { showPreferencesDialog(); } else if (evt.getDescription().startsWith("filters")) { showFilterDialog(); } else if (evt.getDescription().startsWith("categories")) { showCategoriesDialog(); } else if (evt.getDescription().startsWith("overview")) { showHelp(); } else if (evt.getURL() != null) { try { // launch a browser with the appropriate URL Browser.open(evt.getURL().toString()); } catch (InterruptedException e) { System.out.println("Error launching external browser."); } catch (IOException e) { System.out.println("I/O error launching external browser." + e.getMessage()); e.printStackTrace(); } } } }); htmlView = new ViewScrollPane(htmlPane, runningAsVisualVMPlugin); ViewScrollPane emptyView = new ViewScrollPane(emptyPane, runningAsVisualVMPlugin); // create the top split pane topSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); topSplitPane.setLeftComponent(emptyView); topSplitPane.setDividerSize(Const.DIVIDER_SIZE); topSplitPane.setContinuousLayout(true); //Add the scroll panes to a split pane. splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); splitPane.setBottomComponent(htmlView); splitPane.setTopComponent(topSplitPane); splitPane.setDividerSize(Const.DIVIDER_SIZE); splitPane.setContinuousLayout(true); if (this.runningAsVisualVMPlugin) { setOpaque(true); setBackground(Color.WHITE); setBorder(BorderFactory.createEmptyBorder(6, 0, 3, 0)); topSplitPane.setBorder(BorderFactory.createEmptyBorder()); topSplitPane.setOpaque(false); topSplitPane.setBackground(Color.WHITE); htmlPane.setBorder(BorderFactory.createEmptyBorder()); htmlPane.setOpaque(false); htmlPane.setBackground(Color.WHITE); splitPane.setBorder(BorderFactory.createEmptyBorder()); splitPane.setOpaque(false); splitPane.setBackground(Color.WHITE); } Dimension minimumSize = new Dimension(200, 50); htmlView.setMinimumSize(minimumSize); emptyView.setMinimumSize(minimumSize); //Add the split pane to this panel. add(htmlView, BorderLayout.CENTER); statusBar = new StatusBar(!(asJConsolePlugin || asVisualVMPlugin)); add(statusBar, BorderLayout.SOUTH); firstFile = true; setFileOpen(false); if (!runningAsVisualVMPlugin) { setShowToolbar(PrefManager.get().getShowToolbar()); } if (firstFile && runningAsVisualVMPlugin) { // init filechooser fc = new JFileChooser(); fc.setMultiSelectionEnabled(true); fc.setCurrentDirectory(PrefManager.get().getSelectedPath()); } } private void getLogfileFromClipboard() { Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); String text = null; try { if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) { text = (String) t.getTransferData(DataFlavor.stringFlavor); } } catch (UnsupportedFlavorException | IOException ex) { ex.printStackTrace(); } if (text != null) { if (topNodes == null) { initDumpDisplay(text); } else { addDumpStream(new ByteArrayInputStream(text.getBytes()), "Clipboard at " + new Date(System.currentTimeMillis()), false); addToLogfile(text); if (this.getRootPane() != null) { this.getRootPane().revalidate(); } displayContent(null); } if (!this.runningAsVisualVMPlugin) { getMainMenu().getFindLRThreadsToolBarButton().setEnabled(true); getMainMenu().getExpandButton().setEnabled(true); getMainMenu().getCollapseButton().setEnabled(true); } } } private String parseWelcomeURL(InputStream is) { BufferedReader br = null; String resultString = null; StringBuilder result = new StringBuilder(); try { br = new BufferedReader(new InputStreamReader(is)); while (br.ready()) { result.append(br.readLine()); result.append('\n'); } resultString = result.toString(); resultString = resultString.replaceFirst("./important.png", TDA.class.getResource("/doc/important.png").toString()); resultString = resultString.replaceFirst("./logo.png", TDA.class.getResource("/doc/logo.png").toString()); resultString = resultString.replaceFirst("./fileopen.png", TDA.class.getResource("/doc/fileopen.png").toString()); resultString = resultString.replaceFirst("./settings.png", TDA.class.getResource("/doc/settings.png").toString()); resultString = resultString.replaceFirst("./help.png", TDA.class.getResource("/doc/help.png").toString()); resultString = resultString.replaceFirst("<!-- ##tipofday## -->", TipOfDay.getTipOfDay()); resultString = resultString.replaceFirst("<!-- ##recentlogfiles## -->", getAsTable("openlogfile://", PrefManager.get().getRecentFiles())); resultString = resultString.replaceFirst("<!-- ##recentsessions## -->", getAsTable("opensession://", PrefManager.get().getRecentSessions())); } catch (IllegalArgumentException | IOException ex) { // hack to prevent crashing of the app because off unparsed replacer. ex.printStackTrace(); } finally { try { if (br != null) { br.close(); is.close(); } } catch (IOException ex) { ex.printStackTrace(); } } // remove unparsed replacers. resultString = resultString.replaceFirst("<!-- ##tipofday## -->", ""); resultString = resultString.replaceFirst("<!-- ##recentlogfiles## -->", ""); resultString = resultString.replaceFirst("<!-- ##recentsessions## -->", ""); return (resultString); } /** * convert the given elements into a href-table to be included into the * welcome page. Only last four elements are taken. * * @param prefix link prefix to use * @param elements list of elements. * @return given elements as table. */ private String getAsTable(String prefix, String[] elements) { StringBuilder result = new StringBuilder(); int from = elements.length > 4 ? elements.length - 4 : 0; for (int i = from; i < elements.length; i++) { if (elements[i].trim().length() > 0) { // remove backslashes as they confuse the html display. String elem = PATTERN_B_SLASHES.matcher(elements[i]).replaceAll("/"); result.append("<tr><td width=\"20px\"></td><td><a href=\""); result.append(prefix); result.append(elem); result.append("\">"); result.append(cutLink(elem, 80)); result.append("</a></td></tr>\n"); } } return (result.toString()); } /** * cut the given link string to the specified length + three dots. * * @param link * @param len * @return cut link or original link if link.length() <= len */ private String cutLink(String link, int len) { if (link.length() > len) { return (link.substring(0, len / 2) + "..." + link.substring(link.length() - (len / 2))); } return (link); } /** * request jmx dump */ public LogFileContent addMXBeanDump() { String dump = mBeanDumper.threadDump(); String locks = mBeanDumper.findDeadlock(); // if deadlocks were found, append them to dump output. if (!Strings.isNullOrEmpty(locks)) { dump += '\n' + locks; } //System.out.println(dump); if (topNodes == null) { initDumpDisplay(null); } addDumpStream(new ByteArrayInputStream(dump.getBytes()), "Logfile", false); dumpCounter++; LogFileContent lfc = addToLogfile(dump); if (this.getRootPane() != null) { this.getRootPane().revalidate(); } tree.setShowsRootHandles(false); displayContent(null); if (!this.runningAsVisualVMPlugin) { getMainMenu().getFindLRThreadsToolBarButton().setEnabled(true); getMainMenu().getExpandButton().setEnabled(true); getMainMenu().getCollapseButton().setEnabled(true); } return (lfc); } private LogFileContent addToLogfile(String dump) { ((LogFileContent) logFile.getUserObject()).appendToContentBuffer(dump); return (((LogFileContent) logFile.getUserObject())); } /** * create file filter for session files. * * @return file filter instance. */ private static FileFilter getSessionFilter() { return (new FileFilter() { public boolean accept(File arg0) { return (arg0 != null && (arg0.isDirectory() || arg0.getName().endsWith("tsf"))); } public String getDescription() { return ("TDA Session Files"); } }); } /** * initializes session file chooser, if not already done. */ private static void initSessionFc() { sessionFc = new JFileChooser(); sessionFc.setMultiSelectionEnabled(true); sessionFc.setCurrentDirectory(PrefManager.get().getSelectedPath()); if ((PrefManager.get().getPreferredSizeFileChooser().height > 0)) { sessionFc.setPreferredSize(PrefManager.get().getPreferredSizeFileChooser()); } sessionFc.setFileFilter(getSessionFilter()); sessionFc.setSelectedFile(null); } /** * expand all dump nodes in the root tree * * @param expand true=expand, false=collapse. */ public void expandAllDumpNodes(boolean expand) { TreeNode root = (TreeNode) tree.getModel().getRoot(); expandAll(tree, new TreePath(root), expand); } /** * expand all nodes of the currently selected category, only works for tree categories. */ private void expandAllCatNodes(boolean expand) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); JTree catTree = (JTree) ((TreeCategory) node.getUserObject()).getCatComponent(this); if (expand) { for (int i = 0; i < catTree.getRowCount(); i++) { catTree.expandRow(i); } } else { for (int i = 0; i < catTree.getRowCount(); i++) { catTree.collapseRow(i); } } } /** * show help dialog. */ private void showHelp() { HelpViewer.show(getFrame()); } /** * sort monitors by thread amount */ private void sortCatByThreads() { DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); ((TreeCategory) node.getUserObject()).sort(new MonitorComparator()); displayCategory(node.getUserObject()); } /** * expand or collapse all nodes of the specified tree * * @param catTree the tree to expand all/collapse all * @param parent the parent to start with * @param expand expand=true, collapse=false */ private void expandAll(JTree catTree, TreePath parent, boolean expand) { // Traverse children TreeNode node = (TreeNode) parent.getLastPathComponent(); if (node.getChildCount() >= 0) { for (Enumeration e = node.children(); e.hasMoreElements();) { TreeNode n = (TreeNode) e.nextElement(); TreePath path = parent.pathByAddingChild(n); expandAll(catTree, path, expand); } } if (parent.getPathCount() > 1) { // Expansion or collapse must be done bottom-up if (expand) { catTree.expandPath(parent); } else { catTree.collapsePath(parent); } } } private void saveSession() { initSessionFc(); int returnVal = sessionFc.showSaveDialog(this.getRootPane()); sessionFc.setPreferredSize(sessionFc.getSize()); PrefManager.get().setPreferredSizeFileChooser(sessionFc.getSize()); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = sessionFc.getSelectedFile(); // check if file has a suffix if (!file.getName().contains(".")) { file = new File(file.getAbsolutePath() + ".tsf"); } int selectValue = 0; if (file.exists()) { Object[] options = { "Overwrite", "Cancel" }; selectValue = JOptionPane.showOptionDialog(null, "<html><body>File exists<br><b>" + file + "</b></body></html>", "Confirm overwrite", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); } if (selectValue == 0) { ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new GZIPOutputStream(new FileOutputStream(file))); oos.writeObject(dumpFile); oos.writeObject(topNodes); oos.writeObject(dumpStore); } catch (IOException ex) { ex.printStackTrace(); } finally { IOUtils.closeQuietly(oos); } PrefManager.get().addToRecentSessions(file.getAbsolutePath()); } } } private void openSession() { initSessionFc(); int returnVal = sessionFc.showOpenDialog(this.getRootPane()); sessionFc.setPreferredSize(sessionFc.getSize()); PrefManager.get().setPreferredSizeFileChooser(sessionFc.getSize()); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = sessionFc.getSelectedFile(); if ((file.exists())) { openSession(file, false); } } } /** * open the specified session * * @param file */ private void openSession(File file, boolean isRecent) { try { loadSession(file, isRecent); } catch (IOException ex) { JOptionPane.showMessageDialog(this.getRootPane(), "Error opening " + ex.getMessage() + '.', "Error opening session", JOptionPane.ERROR_MESSAGE); } } private void loadSession(File file, boolean isRecent) throws IOException { final ObjectInputStream ois = new ObjectInputStream(new ProgressMonitorInputStream(this, "Opening session " + file, new GZIPInputStream(new FileInputStream(file)))); setFileOpen(true); firstFile = false; resetMainPanel(); initDumpDisplay(null); final SwingWorker worker = new SwingWorker() { @SuppressWarnings("unchecked") public Object construct() { synchronized (syncObject) { try { dumpFile = (String) ois.readObject(); topNodes = (Vector<DefaultMutableTreeNode>) ois.readObject(); dumpStore = (DumpStore) ois.readObject(); ois.close(); } catch (IOException | ClassNotFoundException ex) { ex.printStackTrace(); } finally { try { ois.close(); } catch (IOException ex) { ex.printStackTrace(); } } createTree(); } return null; } }; worker.start(); if (!isRecent) { PrefManager.get().addToRecentSessions(file.getAbsolutePath()); } } private void setShowToolbar(boolean state) { if (state) { add(getMainMenu().getToolBar(), BorderLayout.PAGE_START); } else { remove(getMainMenu().getToolBar()); } revalidate(); PrefManager.get().setShowToolbar(state); } /** * tries the native look and feel on mac and windows and metal on unix (gtk still * isn't looking that nice, even in 1.6) */ private void setupLookAndFeel() { try { String plaf = "Mac,Windows,Metal"; if (System.getProperty("os.name").startsWith("Linux")) { plaf = "GTK,Mac,Windows,Metal"; } // this line needs to be implemented in order to make L&F work properly UIManager.getLookAndFeelDefaults().put("ClassLoader", getClass().getClassLoader()); // query list of L&Fs UIManager.LookAndFeelInfo[] plafs = UIManager.getInstalledLookAndFeels(); if (!Strings.isNullOrEmpty(plaf)) { String[] instPlafs = plaf.split(","); UIManager.LookAndFeelInfo currentLAFI = null; search: for (final String instPlaf : instPlafs) { for (final UIManager.LookAndFeelInfo plaf1 : plafs) { currentLAFI = plaf1; if (currentLAFI.getName().startsWith(instPlaf)) { UIManager.setLookAndFeel(currentLAFI.getClassName()); // setup SANS_SERIF setUIFont(new FontUIResource("SansSerif", Font.PLAIN, Const.FONT_SIZE)); break search; } } } } if (plaf.startsWith("GTK")) { setFontSizeModifier(2); } } catch (Exception except) { setUIFont(new FontUIResource("SansSerif", Font.PLAIN, 12)); } } /** * init the basic display for showing dumps * * @param content initial logfile content may also be parsed, can also be null. * only used for clipboard operations. */ public void initDumpDisplay(String content) { // clear tree dumpStore = new DumpStore(); topNodes = new Vector<>(); if (!runningAsJConsolePlugin && !runningAsVisualVMPlugin) { getMainMenu().getLongMenuItem().setEnabled(true); getMainMenu().getSaveSessionMenuItem().setEnabled(true); getMainMenu().getExpandButton().setEnabled(true); getMainMenu().getCollapseButton().setEnabled(true); getMainMenu().getFindLRThreadsToolBarButton().setEnabled(true); getMainMenu().getCloseAllMenuItem().setEnabled(true); getMainMenu().getExpandAllMenuItem().setEnabled(true); getMainMenu().getCollapseAllMenuItem().setEnabled(true); } if (!runningAsJConsolePlugin || (dumpFile != null)) { if (dumpFile != null) { addDumpFile(); } else if (content != null) { addDumpStream(new ByteArrayInputStream(content.getBytes()), "Clipboard at " + new Date(System.currentTimeMillis()), false); addToLogfile(content); } } if (runningAsJConsolePlugin || runningAsVisualVMPlugin || isFileOpen()) { if (topSplitPane.getDividerLocation() <= 0) { topSplitPane.setDividerLocation(200); } // change from html view to split pane remove(0); revalidate(); htmlPane.setText(""); splitPane.setBottomComponent(htmlView); add(splitPane, BorderLayout.CENTER); if (PrefManager.get().getDividerPos() > 0) { splitPane.setDividerLocation(PrefManager.get().getDividerPos()); } else { // set default divider location splitPane.setDividerLocation(100); } revalidate(); } } /** * add the set dumpFileStream to the tree */ private void addDumpFile() { addDumpFile(dumpFile); } /** * add the set dumpFileStream to the tree */ public void addDumpFile(String filePath) { String[] file = new String[1]; file[0] = filePath; addDumpFiles(file); } private boolean isLogfileSizeOk(String fileName) { File file = new File(fileName); return (file.isFile() && ((PrefManager.get().getMaxLogfileSize() == 0) || (file.length() <= (PrefManager.get().getMaxLogfileSize() * 1024)))); } /** * add the set dumpFileStream to the tree */ private void addDumpFiles(String[] files) { for (final String file : files) { try { dumpCounter = 1; addDumpStream(new FileInputStream(file), file, true); } catch (FileNotFoundException ex) { JOptionPane.showMessageDialog(this.getRootPane(), "Error opening " + ex.getMessage() + '.', "Error opening file", JOptionPane.ERROR_MESSAGE); } } } private void addDumpStream(InputStream inputStream, String file, boolean withLogfile) { final InputStream parseFileStream = new ProgressMonitorInputStream(this, "Parsing " + file, inputStream); //Create the nodes. if (!runningAsJConsolePlugin || topNodes.size() == 0) { topNodes.add(new DefaultMutableTreeNode(new Logfile(file))); } final DefaultMutableTreeNode top = topNodes.get(topNodes.size() - 1); if ((!withLogfile && logFile == null) || isLogfileSizeOk(file)) { logFile = new DefaultMutableTreeNode(new LogFileContent(file)); if (!runningAsVisualVMPlugin) { top.add(logFile); } } setFileOpen(true); final SwingWorker worker = new SwingWorker() { public Object construct() { synchronized (syncObject) { int divider = topSplitPane.getDividerLocation(); addThreadDumps(top, parseFileStream); createTree(); tree.expandRow(1); topSplitPane.setDividerLocation(divider); } return null; } }; worker.start(); } protected void createTree() { //Create a tree that allows multiple selection at a time. if (topNodes.size() == 1) { treeModel = new DefaultTreeModel((DefaultMutableTreeNode) topNodes.get(0)); tree = new JTree(treeModel); tree.setRootVisible(!runningAsJConsolePlugin && !runningAsVisualVMPlugin); addTreeListener(tree); if (!runningAsJConsolePlugin && !runningAsVisualVMPlugin) { frame.setTitle("TDA - Thread Dumps of " + dumpFile); } } else { DefaultMutableTreeNode root = new DefaultMutableTreeNode("Thread Dump Nodes"); treeModel = new DefaultTreeModel(root); for (Object topNode : topNodes) { root.add((DefaultMutableTreeNode) topNode); } tree = new JTree(root); tree.setRootVisible(false); addTreeListener(tree); if (!runningAsJConsolePlugin && !runningAsVisualVMPlugin) { if (!frame.getTitle().endsWith("...")) { frame.setTitle(frame.getTitle() + " ..."); } } } tree.setShowsRootHandles(true); tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); tree.setCellRenderer(new TreeRenderer()); //Create the scroll pane and add the tree to it. ViewScrollPane treeView = new ViewScrollPane(tree, runningAsVisualVMPlugin); topSplitPane.setLeftComponent(treeView); Dimension minimumSize = new Dimension(200, 50); treeView.setMinimumSize(minimumSize); //Listen for when the selection changes. tree.addTreeSelectionListener(this); if (!runningAsJConsolePlugin && !runningAsVisualVMPlugin) { dt = new DropTarget(tree, new FileDropTargetListener()); } createPopupMenu(); } /** * add a tree listener for enabling/disabling menu and toolbar ICONS. * * @param tree JTree */ private void addTreeListener(JTree tree) { tree.addTreeSelectionListener(new TreeSelectionListener() { ViewScrollPane emptyView = null; public void valueChanged(TreeSelectionEvent e) { getMainMenu().getCloseMenuItem().setEnabled(e.getPath() != null); if (getMainMenu().getCloseToolBarButton() != null) { getMainMenu().getCloseToolBarButton().setEnabled(e.getPath() != null); } // reset right pane of the top view: if (emptyView == null) { JEditorPane emptyPane = new JEditorPane("text/html", "<html><body bgcolor=\"ffffff\"></body></html>"); emptyPane.setEditable(false); emptyPane.setSize(Const.EMPTY_DIMENSION); emptyView = new ViewScrollPane(emptyPane, runningAsVisualVMPlugin); } if (e.getPath() == null || !(((DefaultMutableTreeNode) e.getPath().getLastPathComponent()) .getUserObject() instanceof Category)) { resetPane(); } } private void resetPane() { final Rectangle bounds = topSplitPane.getBounds(); final int width = bounds.width; int dividerLocation = topSplitPane.getDividerLocation(); if (width - dividerLocation < Const.MIN_RIGHT_PANE_SIZE && width - Const.MIN_RIGHT_PANE_SIZE > 300) { dividerLocation = width - Const.MIN_RIGHT_PANE_SIZE; } topSplitPane.setRightComponent(emptyView); topSplitPane.setDividerLocation(dividerLocation); } }); } private boolean threadDisplay = false; private void setThreadDisplay(boolean value) { threadDisplay = value; /*if(!value) { // clear thread pane topSplitPane.setRightComponent(null); }*/ } public boolean isThreadDisplay() { return (threadDisplay); } /** * Required by TreeSelectionListener interface. */ public void valueChanged(TreeSelectionEvent e) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath().getLastPathComponent(); if (node == null) { return; } Object nodeInfo = node.getUserObject(); if (nodeInfo instanceof ThreadInfo) { displayThreadInfo(nodeInfo); setThreadDisplay(true); } else if (nodeInfo instanceof ThreadDumpInfo) { displayThreadDumpInfo(nodeInfo); } else if (nodeInfo instanceof HistogramInfo) { HistogramInfo tdi = (HistogramInfo) nodeInfo; displayTable((HistogramTableModel) tdi.content); setThreadDisplay(false); } else if (nodeInfo instanceof LogFileContent) { displayLogFileContent(nodeInfo); } else if (nodeInfo instanceof Logfile && ((String) ((Logfile) nodeInfo).getContent()).startsWith("Thread Dumps")) { displayLogFile(); setThreadDisplay(false); } else if (nodeInfo instanceof Category) { displayCategory(nodeInfo); setThreadDisplay(true); } else { setThreadDisplay(false); displayContent(null); } } /** * process table selection events (thread display) * * @param e the event to process. */ public void valueChanged(ListSelectionEvent e) { //displayCategory(e.getFirstIndex()); ThreadsTableSelectionModel ttsm = (ThreadsTableSelectionModel) e.getSource(); TableSorter ts = (TableSorter) ttsm.getTable().getModel(); int[] rows = ttsm.getTable().getSelectedRows(); StringBuffer sb = new StringBuffer(); for (final int row : rows) { appendThreadInfo(sb, ((ThreadsTableModel) ts.getTableModel()).getInfoObjectAtRow(ts.modelIndex(row))); } displayContent(sb.toString()); setThreadDisplay(true); } private void displayThreadInfo(Object nodeInfo) { StringBuffer sb = new StringBuffer(""); appendThreadInfo(sb, nodeInfo); displayContent(sb.toString()); } private void appendThreadInfo(StringBuffer sb, Object nodeInfo) { ThreadInfo ti = (ThreadInfo) nodeInfo; if (ti.getInfo() != null) { sb.append(ti.getInfo()); sb.append(ti.getContent()); } else { sb.append(ti.getContent()); } } /** * display thread dump information for the give node object. * * @param nodeInfo */ private void displayThreadDumpInfo(Object nodeInfo) { ThreadDumpInfo ti = (ThreadDumpInfo) nodeInfo; displayContent(ti.getOverview()); } private void displayLogFile() { if (splitPane.getBottomComponent() != htmlView) { splitPane.setBottomComponent(htmlView); } htmlPane.setContentType("text/html"); htmlPane.setText(""); htmlPane.setCaretPosition(0); threadDisplay = false; statusBar.setInfoText(AppInfo.getStatusBarInfo()); } private void displayLogFileContent(Object nodeInfo) { int dividerLocation = splitPane.getDividerLocation(); if (splitPane.getBottomComponent() != jeditPane) { if (jeditPane == null) { initJeditView(); } splitPane.setBottomComponent(jeditPane); } LogFileContent lfc = (LogFileContent) nodeInfo; jeditPane.setText(lfc.getContent()); jeditPane.setCaretPosition(0); splitPane.setDividerLocation(dividerLocation); statusBar.setInfoText(AppInfo.getStatusBarInfo()); } /** * initialize the base components needed for the jedit view of the * log file */ private void initJeditView() { jeditPane = new JEditTextArea(); jeditPane.setEditable(false); jeditPane.setCaretVisible(false); jeditPane.setCaretBlinkEnabled(false); jeditPane.setRightClickPopup(new PopupMenu(jeditPane, this, runningAsVisualVMPlugin)); jeditPane.getInputHandler().addKeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0), (ActionListener) jeditPane.getRightClickPopup()); jeditPane.getInputHandler().addKeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_MASK), (ActionListener) jeditPane.getRightClickPopup()); jeditPane.setFont(SANS_SERIF); } /** * display selected category in upper right frame */ private void displayCategory(Object nodeInfo) { final Category cat = ((Category) nodeInfo); topSplitPane.getLeftComponent().setPreferredSize(topSplitPane.getLeftComponent().getSize()); boolean needDividerPos = false; Dimension size = null; if (topSplitPane.getRightComponent() != null) { size = topSplitPane.getRightComponent().getSize(); } else { needDividerPos = true; } setThreadDisplay(true); final JScrollPane lastView = cat.getLastView(); if (lastView == null) { JComponent catComp = cat.getCatComponent(this); if (cat.getName().startsWith("Monitors") || cat.getName().startsWith("Threads blocked by Monitors")) { catComp.addMouseListener(getMonitorsPopupMenu()); } else { catComp.addMouseListener(getCatPopupMenu()); } final ViewScrollPane dumpView = new ViewScrollPane(catComp, runningAsVisualVMPlugin); if (size != null) { dumpView.setPreferredSize(size); } topSplitPane.setRightComponent(dumpView); cat.setLastView(dumpView); } else { if (size != null) { lastView.setPreferredSize(size); } else { lastView.setMinimumSize(Const.EMPTY_DIMENSION); lastView.setPreferredSize(Const.EMPTY_DIMENSION); } topSplitPane.setRightComponent(lastView); } if (cat.getCurrentlySelectedUserObject() != null) { displayThreadInfo(cat.getCurrentlySelectedUserObject()); } else { displayContent(null); } if (needDividerPos) { topSplitPane.setDividerLocation(PrefManager.get().getTopDividerPos()); } if (cat.howManyFiltered() > 0) { statusBar.setInfoText("Filtered " + cat.howManyFiltered() + " elements in this category. Showing remaining " + cat.showing() + " elements."); } else { statusBar.setInfoText(AppInfo.getStatusBarInfo()); } displayContent(cat.getInfo()); } private void displayContent(String text) { if (splitPane.getBottomComponent() != htmlView) { splitPane.setBottomComponent(htmlView); } if (text != null) { htmlPane.setContentType("text/html"); htmlPane.setText(text); htmlPane.setCaretPosition(0); } else { htmlPane.setText(""); } } private void displayTable(HistogramTableModel htm) { setThreadDisplay(false); htm.setFilter(""); htm.setShowHotspotClasses(PrefManager.get().getShowHotspotClasses()); TableSorter ts = new TableSorter(htm); histogramTable = new JTable(ts); ts.setTableHeader(histogramTable.getTableHeader()); histogramTable.getColumnModel().getColumn(0).setPreferredWidth(700); final ViewScrollPane tableView = new ViewScrollPane(histogramTable, runningAsVisualVMPlugin); JPanel histogramView = new JPanel(new BorderLayout()); JPanel histoStatView = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 0)); JLabel infoLabel = new JLabel( NumberFormat.getInstance().format(htm.getRowCount()) + " classes and base types"); infoLabel.setFont(SANS_SERIF); histoStatView.add(infoLabel); infoLabel = new JLabel(NumberFormat.getInstance().format(htm.getBytes()) + " bytes"); infoLabel.setFont(SANS_SERIF); histoStatView.add(infoLabel); infoLabel = new JLabel(NumberFormat.getInstance().format(htm.getInstances()) + " live objects"); infoLabel.setFont(SANS_SERIF); histoStatView.add(infoLabel); if (htm.isOOM()) { infoLabel = new JLabel("<html><b>OutOfMemory found!</b>"); infoLabel.setFont(SANS_SERIF); histoStatView.add(infoLabel); } if (htm.isIncomplete()) { infoLabel = new JLabel("<html><b>Class Histogram is incomplete! (broken logfile?)</b>"); infoLabel.setFont(SANS_SERIF); histoStatView.add(infoLabel); } JPanel filterPanel = new JPanel(new FlowLayout()); infoLabel = new JLabel("Filter-Expression"); infoLabel.setFont(SANS_SERIF); filterPanel.add(infoLabel); filter = new JTextField(30); filter.setFont(SANS_SERIF); filter.addCaretListener(new FilterListener(htm)); filterPanel.add(infoLabel); filterPanel.add(filter); checkCase = new JCheckBox(); checkCase.addChangeListener(new CheckCaseListener(htm)); infoLabel = new JLabel("Ignore Case"); infoLabel.setFont(SANS_SERIF); filterPanel.add(infoLabel); filterPanel.add(checkCase); histoStatView.add(filterPanel); histogramView.add(histoStatView, BorderLayout.SOUTH); histogramView.add(tableView, BorderLayout.CENTER); histogramView.setPreferredSize(splitPane.getBottomComponent().getSize()); splitPane.setBottomComponent(histogramView); } private class FilterListener implements CaretListener { HistogramTableModel htm; String currentText = ""; FilterListener(HistogramTableModel htm) { this.htm = htm; } public void caretUpdate(CaretEvent event) { if (!filter.getText().equals(currentText)) { htm.setFilter(filter.getText()); histogramTable.revalidate(); } } } private class CheckCaseListener implements ChangeListener { HistogramTableModel htm; CheckCaseListener(HistogramTableModel htm) { this.htm = htm; } public void stateChanged(ChangeEvent e) { htm.setIgnoreCase(checkCase.isSelected()); histogramTable.revalidate(); } } private void addThreadDumps(DefaultMutableTreeNode top, InputStream dumpFileStream) { DumpParser dp = null; try { String fileName = top.getUserObject().toString(); Map<String, Map<String, String>> dumpMap = null; if (runningAsJConsolePlugin || runningAsVisualVMPlugin) { dumpMap = dumpStore.getFromDumpFiles(fileName); } if (dumpMap == null) { dumpMap = new HashMap<>(); dumpStore.addFileToDumpFiles(fileName, dumpMap); } dp = DumpParserFactory.get().getDumpParserForLogfile(dumpFileStream, dumpMap, runningAsJConsolePlugin, dumpCounter); ((Logfile) top.getUserObject()).setUsedParser(dp); while ((dp != null) && dp.hasMoreDumps()) { top.add(dp.parseNext()); if (!isFoundClassHistogram) { isFoundClassHistogram = dp.isFoundClassHistograms(); } } } finally { if (dp != null) { try { dp.close(); } catch (IOException ex) { ex.printStackTrace(); } } } } /** * navigate to the currently selected dump in logfile */ private void navigateToDumpInLogfile() { Object userObject = ((DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent()) .getUserObject(); if (userObject instanceof ThreadDumpInfo) { ThreadDumpInfo ti = (ThreadDumpInfo) userObject; int lineNumber = ti.getLogLine(); // find log file node. TreePath selPath = tree.getSelectionPath(); while (selPath != null && !checkNameFromNode((DefaultMutableTreeNode) selPath.getLastPathComponent(), File.separator)) { selPath = selPath.getParentPath(); } tree.setSelectionPath(selPath); tree.scrollPathToVisible(selPath); if (selPath == null) { return; } Enumeration childs = ((DefaultMutableTreeNode) selPath.getLastPathComponent()).children(); boolean found = false; DefaultMutableTreeNode logfileContent = null; while (!found && childs.hasMoreElements()) { logfileContent = (DefaultMutableTreeNode) childs.nextElement(); found = logfileContent.getUserObject() instanceof LogFileContent; } if (found) { TreePath monitorPath = new TreePath(logfileContent.getPath()); tree.setSelectionPath(monitorPath); tree.scrollPathToVisible(monitorPath); displayLogFileContent(logfileContent.getUserObject()); jeditPane.setFirstLine(lineNumber - 1); } } } /** * navigate to monitor * * @param monitorLink the monitor link to navigate to */ private void navigateToMonitor(String monitorLink) { String monitor = monitorLink.substring(monitorLink.lastIndexOf('/') + 1); // find monitor node for this thread info DefaultMutableTreeNode dumpNode; if (monitorLink.indexOf("Dump No.") > 0) { dumpNode = getDumpRootNode( monitorLink.substring(monitorLink.indexOf('/') + 1, monitorLink.lastIndexOf('/')), (DefaultMutableTreeNode) tree.getLastSelectedPathComponent()); } else { dumpNode = getDumpRootNode((DefaultMutableTreeNode) tree.getLastSelectedPathComponent()); } final Enumeration children = dumpNode.children(); DefaultMutableTreeNode monitorNode = null; DefaultMutableTreeNode monitorWithoutLocksNode = null; while (children.hasMoreElements()) { DefaultMutableTreeNode child = (DefaultMutableTreeNode) children.nextElement(); if (child.getUserObject() instanceof TreeCategory) { if (((TreeCategory) child.getUserObject()).getName().startsWith("Monitors (")) { monitorNode = child; } else if (((TreeCategory) child.getUserObject()).getName().startsWith("Monitors without")) { monitorWithoutLocksNode = child; } } } // highlight chosen monitor JTree searchTree = (JTree) ((TreeCategory) monitorNode.getUserObject()).getCatComponent(this); TreePath searchPath = searchTree.getNextMatch(monitor, 0, Position.Bias.Forward); if ((searchPath == null) && (monitorWithoutLocksNode != null)) { searchTree = (JTree) ((TreeCategory) monitorWithoutLocksNode.getUserObject()).getCatComponent(this); searchPath = searchTree.getNextMatch(monitor, 0, Position.Bias.Forward); monitorNode = monitorWithoutLocksNode; } if (searchPath != null) { TreePath monitorPath = new TreePath(monitorNode.getPath()); tree.setSelectionPath(monitorPath); tree.scrollPathToVisible(monitorPath); displayCategory(monitorNode.getUserObject()); TreePath threadInMonitor = searchPath .pathByAddingChild(((DefaultMutableTreeNode) searchPath.getLastPathComponent()).getLastChild()); searchTree.setSelectionPath(threadInMonitor); searchTree.scrollPathToVisible(searchPath); searchTree.setSelectionPath(searchPath); } } /** * navigate to root node of currently active dump */ private void navigateToDump() { TreePath currentPath = tree.getSelectionPath(); tree.setSelectionPath(currentPath.getParentPath()); tree.scrollPathToVisible(currentPath.getParentPath()); } /** * navigate to child of currently selected node with the given prefix in name * * @param startsWith node name prefix (e.g. "Threads waiting") */ private void navigateToChild(String startsWith) { TreePath currentPath = tree.getSelectionPath(); DefaultMutableTreeNode dumpNode = (DefaultMutableTreeNode) currentPath.getLastPathComponent(); Enumeration childs = dumpNode.children(); TreePath searchPath = null; while ((searchPath == null) && childs.hasMoreElements()) { DefaultMutableTreeNode child = (DefaultMutableTreeNode) childs.nextElement(); String name = child.toString(); if (name != null && name.startsWith(startsWith)) { searchPath = new TreePath(child.getPath()); } } if (searchPath != null) { tree.makeVisible(searchPath); tree.setSelectionPath(searchPath); tree.scrollPathToVisible(searchPath); } } protected MainMenu getMainMenu() { if ((frame != null) && (frame.getJMenuBar() != null)) { return ((MainMenu) frame.getJMenuBar()); } else { if (pluginMainMenu == null) { pluginMainMenu = new MainMenu(this); } return (pluginMainMenu); } } public void createPopupMenu() { //Create the popup menu. JPopupMenu popup = new JPopupMenu(); JMenuItem menuItem = new JMenuItem("Diff Selection"); menuItem.addActionListener(this); popup.add(menuItem); menuItem = new JMenuItem("Find long running threads..."); menuItem.addActionListener(this); popup.add(menuItem); showDumpMenuItem = new JMenuItem("Show selected Dump in logfile"); showDumpMenuItem.addActionListener(this); showDumpMenuItem.setEnabled(false); if (!runningAsJConsolePlugin && !runningAsVisualVMPlugin) { popup.addSeparator(); menuItem = new JMenuItem("Parse loggc-logfile..."); menuItem.addActionListener(this); if (!PrefManager.get().getForceLoggcLoading()) { menuItem.setEnabled(!isFoundClassHistogram); } popup.add(menuItem); menuItem = new JMenuItem("Close logfile..."); menuItem.addActionListener(this); popup.add(menuItem); popup.addSeparator(); popup.add(showDumpMenuItem); } else { popup.addSeparator(); if (!runningAsVisualVMPlugin) { menuItem = new JMenuItem("Request Thread Dump..."); menuItem.addActionListener(this); popup.add(menuItem); popup.addSeparator(); menuItem = new JMenuItem("Preferences"); menuItem.addActionListener(this); popup.add(menuItem); menuItem = new JMenuItem("Filters"); menuItem.addActionListener(this); popup.add(menuItem); popup.addSeparator(); menuItem = new JMenuItem("Save Logfile..."); menuItem.addActionListener(this); popup.add(menuItem); popup.addSeparator(); menuItem = new JCheckBoxMenuItem("Show Toolbar", PrefManager.get().getShowToolbar()); menuItem.addActionListener(this); popup.add(menuItem); popup.addSeparator(); menuItem = new JMenuItem("Help"); menuItem.addActionListener(this); popup.add(menuItem); popup.addSeparator(); } menuItem = new JMenuItem("About TDA"); menuItem.addActionListener(this); popup.add(menuItem); } //Add listener to the text area so the popup menu can come up. MouseListener popupListener = new PopupListener(popup); tree.addMouseListener(popupListener); } private PopupListener catPopupListener = null; /** * create a instance of this menu for a category */ private PopupListener getCatPopupMenu() { if (catPopupListener == null) { //Create the popup menu. JPopupMenu popup = new JPopupMenu(); JMenuItem menuItem = new JMenuItem("Search..."); menuItem.addActionListener(this); popup.add(menuItem); //Add listener to the text area so the popup menu can come up. catPopupListener = new PopupListener(popup); } return (catPopupListener); } private PopupListener monitorsPopupListener = null; /** * create a instance of this menu for a category */ private PopupListener getMonitorsPopupMenu() { if (monitorsPopupListener == null) { final JPopupMenu popup = new JPopupMenu(); JMenuItem menuItem = new JMenuItem("Search..."); menuItem.addActionListener(this); popup.add(menuItem); popup.addSeparator(); menuItem = new JMenuItem("Expand all nodes"); menuItem.addActionListener(this); popup.add(menuItem); menuItem = new JMenuItem("Collapse all nodes"); menuItem.addActionListener(this); popup.add(menuItem); popup.addSeparator(); menuItem = new JMenuItem("Sort by thread count"); menuItem.addActionListener(this); popup.add(menuItem); //Add listener to the text area so the popup menu can come up. monitorsPopupListener = new PopupListener(popup); } return (monitorsPopupListener); } class PopupListener extends MouseAdapter { JPopupMenu popup; PopupListener(JPopupMenu popupMenu) { popup = popupMenu; } public void mousePressed(MouseEvent e) { maybeShowPopup(e); } public void mouseReleased(MouseEvent e) { maybeShowPopup(e); } private void maybeShowPopup(MouseEvent e) { if (e.isPopupTrigger()) { popup.show(e.getComponent(), e.getX(), e.getY()); showDumpMenuItem.setEnabled((tree.getSelectionPath() != null) && ((DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent()) .getUserObject() instanceof ThreadDumpInfo); } } } /** * check menu and button events. */ public void actionPerformed(ActionEvent e) { if (e.getSource() instanceof JMenuItem) { JMenuItem source = (JMenuItem) (e.getSource()); if (source.getText().substring(1).startsWith(":\\") || source.getText().startsWith("/")) { if (source.getText().endsWith(".tsf")) { try { loadSession(new File(source.getText()), true); } catch (IOException ex) { ex.printStackTrace(); } } else { dumpFile = source.getText(); openFiles(new File[] { new File(dumpFile) }, true); } } else if ("Open...".equals(source.getText())) { chooseFile(); } else if ("Open loggc file...".equals(source.getText())) { openLoggcFile(); } else if ("Save Logfile...".equals(source.getText())) { saveLogFile(); } else if ("Save Session...".equals(source.getText())) { saveSession(); } else if ("Open Session...".equals(source.getText())) { openSession(); } else if ("Preferences".equals(source.getText())) { showPreferencesDialog(); } else if ("Filters".equals(source.getText())) { showFilterDialog(); } else if ("Categories".equals(source.getText())) { showCategoriesDialog(); } else if ("Get Logfile from clipboard".equals(source.getText())) { getLogfileFromClipboard(); } else if ("Exit TDA".equals(source.getText())) { saveState(); frame.dispose(); } else if (ResourceManager.translate("help.contents").equals(source.getText())) { showHelp(); } else if ("Help".equals(source.getText())) { showHelp(); } else if ("Release Notes".equals(source.getText())) { showInfoFile("Release Notes", "doc/README", Const.ICON_DOCUMENT); } else if ("License".equals(source.getText())) { showInfoFile("License Information", "doc/COPYING", Const.ICON_DOCUMENT); } else if ("Forum".equals(source.getText())) { try { Browser.open("https://tda.dev.java.net/servlets/ForumMessageList?forumID=1967"); } catch (Exception ex) { JOptionPane.showMessageDialog(this.getRootPane(), "Error opening TDA Online Forum\nPlease open https://tda.dev.java.net/servlets/ForumMessageList?forumID=1967 in your browser!", "Error", JOptionPane.ERROR_MESSAGE); } } else if ("About TDA".equals(source.getText())) { showInfo(); } else if ("Search...".equals(source.getText())) { showSearchDialog(); } else if ("Parse loggc-logfile...".equals(source.getText())) { parseLoggcLogfile(); } else if ("Find long running threads...".equals(source.getText())) { findLongRunningThreads(); } else if (("Close logfile...".equals(source.getText())) || ("Close...".equals(source.getText()))) { closeCurrentDump(); } else if ("Close all...".equals(source.getText())) { closeAllDumps(); } else if ("Diff Selection".equals(source.getText())) { final TreePath[] paths = tree.getSelectionPaths(); if (paths != null) { if ((paths.length < 2)) { JOptionPane.showMessageDialog(this.getRootPane(), "You must select at least two dumps for getting a diff!\n", "Error", JOptionPane.ERROR_MESSAGE); } else { DefaultMutableTreeNode mergeRoot = fetchTop(tree.getSelectionPath()); Map dumpMap = dumpStore.getFromDumpFiles(mergeRoot.getUserObject().toString()); ((Logfile) mergeRoot.getUserObject()).getUsedParser().mergeDumps(mergeRoot, dumpMap, paths, paths.length, null); createTree(); this.getRootPane().revalidate(); } } } else if ("Show selected Dump in logfile".equals(source.getText())) { navigateToDumpInLogfile(); } else if ("Show Toolbar".equals(source.getText())) { setShowToolbar(((JCheckBoxMenuItem) source).getState()); } else if ("Request Thread Dump...".equals(source.getText())) { addMXBeanDump(); } else if ("Expand all nodes".equals(source.getText())) { expandAllCatNodes(true); } else if ("Collapse all nodes".equals(source.getText())) { expandAllCatNodes(false); } else if ("Sort by thread count".equals(source.getText())) { sortCatByThreads(); } else if ("Expand all Dump nodes".equals(source.getText())) { expandAllDumpNodes(true); } else if ("Collapse all Dump nodes".equals(source.getText())) { expandAllDumpNodes(false); } } else if (e.getSource() instanceof JButton) { JButton source = (JButton) e.getSource(); if ("Open Logfile".equals(source.getToolTipText())) { chooseFile(); } else if ("Close selected Logfile".equals(source.getToolTipText())) { closeCurrentDump(); } else if ("Get Logfile from clipboard".equals(source.getToolTipText())) { getLogfileFromClipboard(); } else if ("Preferences".equals(source.getToolTipText())) { showPreferencesDialog(); } else if ("Find long running threads".equals(source.getToolTipText())) { findLongRunningThreads(); } else if ("Expand all nodes".equals(source.getToolTipText())) { expandAllDumpNodes(true); } else if ("Collapse all nodes".equals(source.getToolTipText())) { expandAllDumpNodes(false); } else if ("Find long running threads".equals(source.getToolTipText())) { findLongRunningThreads(); } else if ("Filters".equals(source.getToolTipText())) { showFilterDialog(); } else if ("Custom Categories".equals(source.getToolTipText())) { showCategoriesDialog(); } else if ("Request a Thread Dump".equals(source.getToolTipText())) { addMXBeanDump(); } else if ("Help".equals(source.getToolTipText())) { showHelp(); } source.setSelected(false); } } private void showInfo() { InfoDialog infoDialog = new InfoDialog(getFrame()); infoDialog.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); //Display the window. infoDialog.pack(); infoDialog.setLocationRelativeTo(getFrame()); infoDialog.setVisible(true); } /** * set the ui SANS_SERIF for all tda stuff (needs to be done for create of objects) * * @param f the SANS_SERIF to user */ private void setUIFont(FontUIResource f) { // // sets the default SANS_SERIF for all Swing components. // ex. // setUIFont (new javax.swing.plaf.FontUIResource("Serif",Font.ITALIC,12)); // Enumeration keys = UIManager.getDefaults().keys(); while (keys.hasMoreElements()) { Object key = keys.nextElement(); Object value = UIManager.get(key); if (value instanceof FontUIResource) { UIManager.put(key, f); } } } /** * display the specified file in a info window. * * @param title title of the info window. * @param file the file to display. */ private void showInfoFile(String title, String file, String icon) { HelpOverviewDialog infoDialog = new HelpOverviewDialog(getFrame(), title, file, TDA.createImageIcon(icon).getImage()); infoDialog.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); //Display the window. infoDialog.pack(); infoDialog.setLocationRelativeTo(getFrame()); infoDialog.setVisible(true); } private JFrame getFrame() { Container owner = this.getParent(); while (owner != null && !(owner instanceof JFrame)) { owner = owner.getParent(); } return (owner != null ? (JFrame) owner : null); } private void showPreferencesDialog() { //Create and set up the window. if (prefsDialog == null) { prefsDialog = new PreferencesDialog(getFrame()); prefsDialog.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); } getFrame().setEnabled(false); //Display the window. prefsDialog.reset(); prefsDialog.pack(); prefsDialog.setLocationRelativeTo(getFrame()); prefsDialog.setVisible(true); } public void showFilterDialog() { //Create and set up the window. if (filterDialog == null) { filterDialog = new FilterDialog(getFrame()); filterDialog.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); } getFrame().setEnabled(false); //Display the window. filterDialog.reset(); filterDialog.pack(); filterDialog.setLocationRelativeTo(getFrame()); filterDialog.setVisible(true); } /** * display categories settings. */ private void showCategoriesDialog() { //Create and set up the window. if (categoriesDialog == null) { categoriesDialog = new CustomCategoriesDialog(getFrame()); categoriesDialog.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); } getFrame().setEnabled(false); //Display the window. categoriesDialog.reset(); categoriesDialog.pack(); categoriesDialog.setLocationRelativeTo(getFrame()); categoriesDialog.setVisible(true); } /** * flag indicates if next file to open will be the first file (so fresh open) * or if a add has to be performed. */ private boolean firstFile = true; /** * save the current logfile (only used in plugin mode) */ public void saveLogFile() { if (fc == null) { fc = new JFileChooser(); fc.setMultiSelectionEnabled(true); fc.setCurrentDirectory(PrefManager.get().getSelectedPath()); } if (firstFile && (PrefManager.get().getPreferredSizeFileChooser().height > 0)) { fc.setPreferredSize(PrefManager.get().getPreferredSizeFileChooser()); } int returnVal = fc.showSaveDialog(this.getRootPane()); fc.setPreferredSize(fc.getSize()); PrefManager.get().setPreferredSizeFileChooser(fc.getSize()); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fc.getSelectedFile(); int selectValue = 0; if (file.exists()) { Object[] options = { "Overwrite", "Cancel" }; selectValue = JOptionPane.showOptionDialog(null, "<html><body>File exists<br><b>" + file + "</b></body></html>", "Confirm overwrite", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); } if (selectValue == 0) { FileOutputStream fos = null; try { fos = new FileOutputStream(file); fos.write(((LogFileContent) logFile.getUserObject()).getContent().getBytes()); fos.flush(); } catch (IOException ex) { ex.printStackTrace(); } finally { try { fos.close(); } catch (IOException ex) { ex.printStackTrace(); } } } } } /** * choose a log file. */ private void chooseFile() { if (firstFile && (PrefManager.get().getPreferredSizeFileChooser().height > 0)) { fc.setPreferredSize(PrefManager.get().getPreferredSizeFileChooser()); } int returnVal = fc.showOpenDialog(this.getRootPane()); fc.setPreferredSize(fc.getSize()); PrefManager.get().setPreferredSizeFileChooser(fc.getSize()); if (returnVal == JFileChooser.APPROVE_OPTION) { File[] files = fc.getSelectedFiles(); openFiles(files, false); } } /** * open the provided files. If isRecent is set to true, passed files * are not added to the recent file list. * * @param files the files array to open * @param isRecent true, if passed files are from recent file list. */ private void openFiles(File[] files, boolean isRecent) { for (final File file : files) { dumpFile = file.getAbsolutePath(); if (!firstFile) { // root nodes are moved down. setRootNodeLevel(1); // do direct add without re-init. addDumpFile(); } else { initDumpDisplay(null); if (isFileOpen()) { firstFile = false; } } if (!isRecent) { PrefManager.get().addToRecentFiles(file.getAbsolutePath()); } } if (isFileOpen()) { this.getRootPane().revalidate(); displayContent(null); } } /** * Returns an ImageIcon, or null if the path was invalid. */ public static ImageIcon createImageIcon(String path) { URL imgURL = TDA.class.getResource("/icons/" + path); if (imgURL != null) { return new ImageIcon(imgURL); } else { System.err.println("Couldn't find file: " + path); return null; } } /** * search for dump root node of for given node * * @param node starting to search for * @return root node returns null, if no root was found. */ private DefaultMutableTreeNode getDumpRootNode(DefaultMutableTreeNode node) { // search for starting node while (node != null && !(node.getUserObject() instanceof ThreadDumpInfo)) { node = (DefaultMutableTreeNode) node.getParent(); } return (node); } /** * get the dump with the given name, starting from the provided node. */ private DefaultMutableTreeNode getDumpRootNode(String dumpName, DefaultMutableTreeNode node) { DefaultMutableTreeNode lastNode = null; // search for starting node while (node != null && !(node.getUserObject() instanceof Logfile)) { lastNode = node; node = (DefaultMutableTreeNode) node.getParent(); } if (node == null) { node = lastNode; } DefaultMutableTreeNode dumpNode = null; if (node != null) { for (int i = 0; i < node.getChildCount(); i++) { Object userObject = ((DefaultMutableTreeNode) node.getChildAt(i)).getUserObject(); if ((userObject instanceof ThreadDumpInfo) && ((ThreadDumpInfo) userObject).getName().startsWith(dumpName)) { dumpNode = (DefaultMutableTreeNode) node.getChildAt(i); break; } } } return (dumpNode); } /** * load a loggc log file based on the current selected thread dump */ private void parseLoggcLogfile() { DefaultMutableTreeNode node = getDumpRootNode((DefaultMutableTreeNode) tree.getLastSelectedPathComponent()); if (node == null) { return; } // get pos of this node in the thread dump hierarchy. int pos = node.getParent().getIndex(node); ((Logfile) ((DefaultMutableTreeNode) node.getParent()).getUserObject()).getUsedParser() .setDumpHistogramCounter(pos); openLoggcFile(); } /** * close the currently selected dump. */ private void closeCurrentDump() { TreePath selPath = tree.getSelectionPath(); while (selPath != null && !(checkNameFromNode((DefaultMutableTreeNode) selPath.getLastPathComponent(), File.separator) || checkNameFromNode((DefaultMutableTreeNode) selPath.getLastPathComponent(), 2, File.separator))) { selPath = selPath.getParentPath(); } Object[] options = { "Close File", "Cancel close" }; String fileName = ((DefaultMutableTreeNode) selPath.getLastPathComponent()).getUserObject().toString(); fileName = fileName.substring(fileName.indexOf(File.separator)); int selectValue = JOptionPane.showOptionDialog(null, "<html><body>Are you sure, you want to close the currently selected dump file<br><b>" + fileName + "</b></body></html>", "Confirm closing...", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); // if first option "close file" is selected. if (selectValue == 0) { // remove stuff from the top nodes topNodes.remove(selPath.getLastPathComponent()); if (topNodes.size() == 0) { // simply do a reinit, as there isn't anything to display removeAll(); revalidate(); init(runningAsJConsolePlugin, runningAsVisualVMPlugin); getMainMenu().getLongMenuItem().setEnabled(false); getMainMenu().getCloseMenuItem().setEnabled(false); getMainMenu().getSaveSessionMenuItem().setEnabled(false); getMainMenu().getCloseToolBarButton().setEnabled(false); getMainMenu().getExpandButton().setEnabled(false); getMainMenu().getCollapseButton().setEnabled(false); getMainMenu().getFindLRThreadsToolBarButton().setEnabled(false); getMainMenu().getCloseAllMenuItem().setEnabled(false); getMainMenu().getExpandAllMenuItem().setEnabled(false); getMainMenu().getCollapseAllMenuItem().setEnabled(false); } else { // rebuild jtree getMainMenu().getCloseMenuItem().setEnabled(false); getMainMenu().getCloseToolBarButton().setEnabled(false); createTree(); } revalidate(); } } /** * close all open dumps */ private void closeAllDumps() { Object[] options = { "Close all", "Cancel close" }; int selectValue = JOptionPane.showOptionDialog(null, "<html><body>Are you sure, you want to close all open dump files", "Confirm closing...", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); // if first option "close file" is selected. if (selectValue == 0) { // remove stuff from the top nodes topNodes = new Vector<>(); // simply do a re-init, as there is anything to display resetMainPanel(); } } /** * reset the main panel to start up */ private void resetMainPanel() { removeAll(); revalidate(); init(runningAsJConsolePlugin, runningAsVisualVMPlugin); revalidate(); getMainMenu().getLongMenuItem().setEnabled(false); getMainMenu().getCloseMenuItem().setEnabled(false); getMainMenu().getSaveSessionMenuItem().setEnabled(false); getMainMenu().getCloseToolBarButton().setEnabled(false); getMainMenu().getExpandButton().setEnabled(false); getMainMenu().getCollapseButton().setEnabled(false); getMainMenu().getFindLRThreadsToolBarButton().setEnabled(false); getMainMenu().getCloseAllMenuItem().setEnabled(false); getMainMenu().getExpandAllMenuItem().setEnabled(false); getMainMenu().getCollapseAllMenuItem().setEnabled(false); } /** * check if name of node starts with passed string * * @param node the node name to check * @param startsWith the string to compare. * @return true if startsWith and beginning of node name matches. */ private boolean checkNameFromNode(DefaultMutableTreeNode node, String startsWith) { return (checkNameFromNode(node, 0, startsWith)); } /** * check if name of node starts with passed string * * @param node the node name to check * @param startIndex the index to start with comparing, 0 if comparing should happen * from the beginning. * @param startsWith the string to compare. * @return true if startsWith and beginning of node name matches. */ private boolean checkNameFromNode(DefaultMutableTreeNode node, int startIndex, String startsWith) { Object info = node.getUserObject(); String result = null; if ((info != null) && (info instanceof AbstractInfo)) { result = ((AbstractInfo) info).getName(); } else if ((info != null) && (info instanceof String)) { result = (String) info; } if (startIndex > 0 && result != null) { result = result.substring(startIndex); } return (result != null && result.startsWith(startsWith)); } /** * open and parse loggc file */ private void openLoggcFile() { int returnVal = fc.showOpenDialog(this.getRootPane()); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fc.getSelectedFile(); final String loggcFile = file.getAbsolutePath(); try { final InputStream loggcFileStream = new ProgressMonitorInputStream(this, "Parsing " + loggcFile, new FileInputStream(loggcFile)); final SwingWorker worker = new SwingWorker() { public Object construct() { try { DefaultMutableTreeNode top = fetchTop(tree.getSelectionPath()); ((Logfile) top.getUserObject()).getUsedParser().parseLoggcFile(loggcFileStream, top); addThreadDumps(top, loggcFileStream); createTree(); getRootPane().revalidate(); displayContent(null); } finally { try { loggcFileStream.close(); } catch (IOException ex) { ex.printStackTrace(); } } return null; } }; worker.start(); } catch (FileNotFoundException ex) { ex.printStackTrace(); } } } /** * find long running threads either in all parsed thread dumps or in marked thread * dump range. */ private void findLongRunningThreads() { TreePath[] paths = tree.getSelectionPaths(); if ((paths == null) || (paths.length < 2)) { JOptionPane.showMessageDialog(this.getRootPane(), "You must select at least two dumps for long thread run detection!\n", "Error", JOptionPane.ERROR_MESSAGE); } else { DefaultMutableTreeNode mergeRoot = fetchTop(tree.getSelectionPath()); Map dumpMap = dumpStore.getFromDumpFiles(mergeRoot.getUserObject().toString()); LongThreadDialog longThreadDialog = new LongThreadDialog(this, paths, mergeRoot, dumpMap); if (frame != null) { frame.setEnabled(false); } //Display the window. longThreadDialog.reset(); longThreadDialog.pack(); longThreadDialog.setLocationRelativeTo(frame); longThreadDialog.setVisible(true); } } private int rootNodeLevel = 0; private int getRootNodeLevel() { return (rootNodeLevel); } private void setRootNodeLevel(int value) { rootNodeLevel = value; } private DefaultMutableTreeNode fetchTop(TreePath pathToRoot) { return ((DefaultMutableTreeNode) pathToRoot.getPathComponent(getRootNodeLevel())); } /** * save the application state to preferences. */ private void saveState() { PrefManager.get().setWindowState(frame.getExtendedState()); PrefManager.get().setSelectedPath(fc.getCurrentDirectory()); PrefManager.get().setPreferredSize(frame.getRootPane().getSize()); PrefManager.get().setWindowPos(frame.getX(), frame.getY()); if (isThreadDisplay()) { PrefManager.get().setTopDividerPos(topSplitPane.getDividerLocation()); PrefManager.get().setDividerPos(splitPane.getDividerLocation()); } PrefManager.get().flush(); } /** * trigger, if a file is opened */ private boolean fileOpen = false; private boolean isFileOpen() { return fileOpen; } private void setFileOpen(boolean value) { fileOpen = value; } /** * Create the GUI and show it. For thread safety, * this method should be invoked from the * event-dispatching thread. */ private static void createAndShowGUI() { //Create and set up the window. frame = new JFrame("TDA - Thread Dump Analyzer"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Image image = Toolkit.getDefaultToolkit().getImage( "TDA.gif" ); Image image = TDA.createImageIcon(Const.ICON_TDA).getImage(); frame.setIconImage(image); frame.getRootPane().setPreferredSize(PrefManager.get().getPreferredSize()); frame.setJMenuBar(new MainMenu(TDA.get(true))); TDA.get(true).init(false, false); //Create and set up the content pane. if (dumpFile != null) { TDA.get(true).initDumpDisplay(null); } TDA.get(true).setOpaque(true); //content panes must be opaque frame.setContentPane(TDA.get(true)); // init filechooser fc = new JFileChooser(); fc.setMultiSelectionEnabled(true); fc.setCurrentDirectory(PrefManager.get().getSelectedPath()); /** * add window listener for persisting state of main frame */ frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { TDA.get(true).saveState(); } public void windowClosed(WindowEvent e) { System.exit(0); } }); frame.setLocation(PrefManager.get().getWindowPos()); //Display the window. frame.pack(); // restore old window settings. frame.setExtendedState(PrefManager.get().getWindowState()); frame.setVisible(true); } /** * display search dialog for current category */ private void showSearchDialog() { // get the currently select category tree DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); JComponent catComp = ((Category) node.getUserObject()).getCatComponent(this); //Create and set up the window. searchDialog = new SearchDialog(getFrame(), catComp); getFrame().setEnabled(false); //Display the window. searchDialog.reset(); searchDialog.pack(); searchDialog.setLocationRelativeTo(getFrame()); searchDialog.setVisible(true); searchDialog.addWindowListener(new WindowAdapter() { public void windowClosed(WindowEvent e) { getFrame().setEnabled(true); } }); } /** * main startup method for TDA */ public static void main(String[] args) { if (args.length > 0) { dumpFile = args[0]; } //Schedule a job for the event-dispatching thread: //creating and showing this application's GUI. SwingUtilities.invokeLater(TDA::createAndShowGUI); } /** * check file menu */ public void menuSelected(MenuEvent e) { JMenu source = (JMenu) e.getSource(); if ((source != null) && "File".equals(source.getText())) { // close menu item only active, if something is selected. getMainMenu().getCloseMenuItem().setEnabled(tree.getSelectionPath() != null); getMainMenu().getCloseToolBarButton().setEnabled(tree.getSelectionPath() != null); } } public void menuDeselected(MenuEvent e) { // nothing to do } public void menuCanceled(MenuEvent e) { // nothing to do } public static String getFontSizeModifier(int add) { String result = String.valueOf(fontSizeModifier + add); if ((fontSizeModifier + add) > 0) { result = "+" + (fontSizeModifier + add); } return (result); } public static void setFontSizeModifier(int value) { fontSizeModifier = value; } /** * handles dragging events for new files to open. */ private class FileDropTargetListener extends DropTargetAdapter { public void drop(DropTargetDropEvent dtde) { try { DataFlavor[] df = dtde.getTransferable().getTransferDataFlavors(); for (final DataFlavor aDf : df) { if (aDf.isMimeTypeEqual("application/x-java-serialized-object")) { dtde.acceptDrop(dtde.getDropAction()); String[] fileStrings = ((String) dtde.getTransferable().getTransferData(aDf)).split("\n"); File[] files = new File[fileStrings.length]; for (int j = 0; j < fileStrings.length; j++) { files[j] = new File(fileStrings[j].substring(7)); } openFiles(files, false); dtde.dropComplete(true); } } } catch (UnsupportedFlavorException | IOException ex) { ex.printStackTrace(); dtde.rejectDrop(); } } } }