/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2001 Intersect Software Corporation This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org */ package net.wotonomy.ui.swing; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.border.EmptyBorder; import javax.swing.table.TableColumn; import net.wotonomy.foundation.NSArray; import net.wotonomy.ui.EOAssociation; import net.wotonomy.ui.EODisplayGroup; import net.wotonomy.ui.swing.components.ButtonPanel; import net.wotonomy.ui.swing.util.WindowUtilities; /** * ReferenceInspector tracks objects until they are garbage collected. Use it to * track objects that you suspect are not being GCed. ReferenceInspector retains * only weak references to the objects that it tracks, so when those weak * references cannot be resolved, the object has been garbage collected. Note * that under some GC implementations, adding a weak reference to an object will * delay garbage collection for that object. * * @author michael@mpowers.net * @version $Revision: 904 $ */ public class ReferenceInspector implements MouseListener, ActionListener { protected JTable table; protected JLabel memoryLabel; static protected EODisplayGroup displayGroup; static protected JFrame window; /* Reference queue for cleared WeakKeys */ private static ReferenceQueue queue = new ReferenceQueue(); // key command to copy contents to clipboard static public final String COPY = "COPY"; /** * Launches a new ReferenceInspector if one does not already exist. */ public ReferenceInspector() { if (window == null) { table = new JTable(); memoryLabel = new JLabel(); displayGroup = new EODisplayGroup(); TableColumn column; TableColumnAssociation assoc; column = new TableColumn(); column.setHeaderValue("Object"); assoc = new TableColumnAssociation(column); assoc.bindAspect(EOAssociation.ValueAspect, displayGroup, ""); assoc.setTable(table); assoc.establishConnection(); column = new TableColumn(); column.setHeaderValue("Address"); column.setMaxWidth(100); assoc = new TableColumnAssociation(column); assoc.bindAspect(EOAssociation.ValueAspect, displayGroup, "identityHashCode"); assoc.setTable(table); assoc.establishConnection(); initLayout(); } window.show(); } /** * Adds the specified object to the ReferenceInspector, launching a new * ReferenceInspector if one does not already exist. */ public ReferenceInspector(Object anObject) { this(); displayGroup.insertObjectAtIndex(new ExtendedWeakReference(anObject, queue), 0); window.show(); } protected void initLayout() { table.addMouseListener(this); // listen for double-clicks JPanel panel = new JPanel(); panel.setBorder(new EmptyBorder(new Insets(10, 10, 10, 10))); panel.setLayout(new BorderLayout(10, 10)); JScrollPane scrollPane = new JScrollPane(table); scrollPane.setPreferredSize(new Dimension(500, 250)); panel.add(scrollPane, BorderLayout.CENTER); ButtonPanel buttonPanel = new ButtonPanel(new String[] { "Update" }); buttonPanel.addActionListener(this); JPanel bottomPanel = new JPanel(); bottomPanel.setLayout(new BorderLayout()); bottomPanel.add(buttonPanel, BorderLayout.EAST); bottomPanel.add(memoryLabel, BorderLayout.CENTER); panel.add(bottomPanel, BorderLayout.SOUTH); window = new JFrame(); window.setTitle("Reference Inspector"); window.getContentPane().add(panel); // javax.swing.Timer timer = new javax.swing.Timer( 10000, this ); // timer.restart(); window.pack(); WindowUtilities.cascade(window); window.show(); } /* * Remove all invalidated entries from the map, that is, remove all entries * whose keys have been discarded. This method should be invoked once by each * public mutator in this class. We don't invoke this method in public accessors * because that can lead to surprising ConcurrentModificationExceptions. */ private static void processQueue() { // System.out.println( "ReferenceInspector.processQueue:"); synchronized (displayGroup) { int idx; WeakReference rk; while ((rk = (WeakReference) queue.poll()) != null) { // System.out.println( "ReferenceInspector.processQueue: removing object: " + rk ); if (rk != null) { idx = displayGroup.displayedObjects().indexOfIdenticalObject(rk); if (idx != NSArray.NotFound) { displayGroup.deleteObjectAtIndex(idx); } } } displayGroup.updateDisplayedObjects(); } } // interface ActionListener public void actionPerformed(ActionEvent evt) { Runtime runtime = Runtime.getRuntime(); runtime.gc(); processQueue(); long totalMemory = runtime.totalMemory() / 1024; long freeMemory = runtime.freeMemory() / 1024; memoryLabel.setText(Long.toString(totalMemory - freeMemory) + "K / " + Long.toString(totalMemory) + "K"); } // interface MouseListener /** * Double click to launch object inspector. */ public void mouseClicked(MouseEvent e) { if (e.getSource() == table) { if (e.getClickCount() > 1) { int row = table.rowAtPoint(e.getPoint()); int col = table.columnAtPoint(e.getPoint()); col = table.convertColumnIndexToModel(col); if (row == -1) return; if (col == 0) // time { } else { } } } } public void mouseReleased(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public class ExtendedWeakReference extends WeakReference { public ExtendedWeakReference(Object referent, ReferenceQueue q) { super(referent, q); } public String toString() { if (get() != null) { return get().toString(); } return null; } public String identityHashCode() { if (get() != null) { return Integer.toHexString(System.identityHashCode(get())); } return null; } } } /* * $Log$ Revision 1.2 2006/02/18 23:19:05 cgruber Update imports and maven * dependencies. * * Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in * eclipse-friendly maven-enabled packages. * * Revision 1.5 2003/08/06 23:07:52 chochos general code cleanup (mostly, * removing unused imports) * * Revision 1.4 2002/03/22 22:39:59 mpowers Now shows the window even if * previously hidden. * * Revision 1.3 2001/10/02 14:22:39 mpowers Now shows used and heap memory * usage. * * Revision 1.2 2001/07/10 16:39:32 mpowers Removed printlns. * * Revision 1.1 2001/07/10 16:32:50 mpowers Adding the reference inspector. * * */