View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2001 Intersect Software Corporation
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.ui.swing;
20  
21  import java.awt.BorderLayout;
22  import java.awt.Dimension;
23  import java.awt.Insets;
24  import java.awt.event.ActionEvent;
25  import java.awt.event.ActionListener;
26  import java.awt.event.MouseEvent;
27  import java.awt.event.MouseListener;
28  import java.lang.ref.ReferenceQueue;
29  import java.lang.ref.WeakReference;
30  
31  import javax.swing.JFrame;
32  import javax.swing.JLabel;
33  import javax.swing.JPanel;
34  import javax.swing.JScrollPane;
35  import javax.swing.JTable;
36  import javax.swing.border.EmptyBorder;
37  import javax.swing.table.TableColumn;
38  
39  import net.wotonomy.foundation.NSArray;
40  import net.wotonomy.ui.EOAssociation;
41  import net.wotonomy.ui.EODisplayGroup;
42  import net.wotonomy.ui.swing.components.ButtonPanel;
43  import net.wotonomy.ui.swing.util.WindowUtilities;
44  
45  /***
46  * ReferenceInspector tracks objects until they are garbage collected.
47  * Use it to track objects that you suspect are not being GCed.
48  * ReferenceInspector retains only weak references to the objects that
49  * it tracks, so when those weak references cannot be resolved, the
50  * object has been garbage collected.  Note that under some GC 
51  * implementations, adding a weak reference to an object will delay
52  * garbage collection for that object.
53  *
54  * @author michael@mpowers.net
55  * @version $Revision: 904 $
56  */
57  
58  public class ReferenceInspector
59      implements MouseListener, ActionListener
60  {
61      protected JTable table;
62      protected JLabel memoryLabel;
63      
64      static protected EODisplayGroup displayGroup;
65      static protected JFrame window;
66  
67      /* Reference queue for cleared WeakKeys */
68      private static ReferenceQueue queue = new ReferenceQueue();
69      
70      // key command to copy contents to clipboard
71      static public final String COPY = "COPY";
72  
73  /***
74  * Launches a new ReferenceInspector if one does not already exist.
75  */
76      public ReferenceInspector()
77      {
78          if ( window == null )
79          {
80              table = new JTable();
81              memoryLabel = new JLabel();            
82      
83              displayGroup = new EODisplayGroup();
84              
85              TableColumn column;
86              TableColumnAssociation assoc;
87              
88              column = new TableColumn();
89              column.setHeaderValue( "Object" );
90      
91              assoc = new TableColumnAssociation( column );
92              assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "" );
93              assoc.setTable( table );
94              assoc.establishConnection();
95              
96              column = new TableColumn();
97              column.setHeaderValue( "Address" );
98              column.setMaxWidth( 100 );
99      
100             assoc = new TableColumnAssociation( column );
101             assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "identityHashCode" );
102             assoc.setTable( table );
103             assoc.establishConnection();
104     
105             initLayout();
106         }
107         window.show();
108     }
109 
110 /***
111 * Adds the specified object to the ReferenceInspector, launching 
112 * a new ReferenceInspector if one does not already exist.
113 */
114     public ReferenceInspector( Object anObject )
115     {
116         this();
117         displayGroup.insertObjectAtIndex( new ExtendedWeakReference( anObject, queue ), 0 );
118         window.show();
119     }
120     
121     protected void initLayout()
122     {
123         table.addMouseListener( this ); // listen for double-clicks
124 
125         JPanel panel = new JPanel();
126         panel.setBorder( new EmptyBorder( new Insets( 10, 10, 10, 10 ) ) );
127         panel.setLayout( new BorderLayout( 10, 10 ) );
128 		
129         JScrollPane scrollPane = new JScrollPane( table );
130         scrollPane.setPreferredSize( new Dimension( 500, 250 ) );
131         panel.add( scrollPane, BorderLayout.CENTER );
132 
133         ButtonPanel buttonPanel = new ButtonPanel( new String[] { "Update" } );
134         buttonPanel.addActionListener( this );
135 
136         JPanel bottomPanel = new JPanel();
137         bottomPanel.setLayout( new BorderLayout() );
138         bottomPanel.add( buttonPanel, BorderLayout.EAST );                
139         bottomPanel.add( memoryLabel, BorderLayout.CENTER );                
140         panel.add( bottomPanel, BorderLayout.SOUTH );
141 
142         window = new JFrame();
143         window.setTitle( "Reference Inspector" );
144         window.getContentPane().add( panel );
145 
146 //        javax.swing.Timer timer = new javax.swing.Timer( 10000, this );
147 //        timer.restart();
148     
149         window.pack();
150         WindowUtilities.cascade( window );
151         window.show();
152     }
153 
154     /* Remove all invalidated entries from the map, that is, remove all entries
155        whose keys have been discarded.  This method should be invoked once by
156        each public mutator in this class.  We don't invoke this method in
157        public accessors because that can lead to surprising
158        ConcurrentModificationExceptions. */
159     private static void processQueue() 
160     {
161 //        System.out.println( "ReferenceInspector.processQueue:");
162         synchronized ( displayGroup )
163         {
164             int idx;
165             WeakReference rk;
166             while ((rk = (WeakReference)queue.poll()) != null) 
167             {
168 //                System.out.println( "ReferenceInspector.processQueue: removing object: " + rk );
169                 if ( rk != null )
170                 {
171                     idx = displayGroup.displayedObjects().indexOfIdenticalObject( rk );                
172                     if ( idx != NSArray.NotFound )
173                     {
174                         displayGroup.deleteObjectAtIndex( idx );
175                     }
176                 }
177             }
178             displayGroup.updateDisplayedObjects();
179         }
180     }
181 
182     // interface ActionListener
183     
184     public void actionPerformed( ActionEvent evt ) 
185     {
186         Runtime runtime = Runtime.getRuntime();
187         runtime.gc();
188         processQueue();
189         
190         long totalMemory = runtime.totalMemory() / 1024;
191         long freeMemory = runtime.freeMemory() / 1024;
192         memoryLabel.setText( 
193             Long.toString( totalMemory - freeMemory ) + "K / " +
194             Long.toString( totalMemory ) + "K" );
195     }
196 
197     // interface MouseListener
198 
199     /***
200     * Double click to launch object inspector.
201     */
202 
203     public void mouseClicked(MouseEvent e)
204     {
205         if ( e.getSource() == table )
206         {
207             if ( e.getClickCount() > 1 )
208             {
209                 int row = table.rowAtPoint( e.getPoint() );
210                 int col = table.columnAtPoint( e.getPoint() );
211                 col = table.convertColumnIndexToModel( col );
212 
213                 if ( row == -1 ) return;
214                 
215                 if ( col == 0 ) // time
216                 {
217                 }
218                 else
219                 {
220                 }
221             }
222         }
223     }
224 
225     public void mouseReleased(MouseEvent e) {}
226     public void mousePressed(MouseEvent e) {}
227     public void mouseEntered(MouseEvent e) {}
228     public void mouseExited(MouseEvent e) {}
229     
230     public class ExtendedWeakReference extends WeakReference
231     {
232         public ExtendedWeakReference(Object referent, ReferenceQueue q) 
233         {
234             super( referent, q );
235         }
236         
237         public String toString() 
238         {
239             if ( get() != null )
240             {
241                 return get().toString();
242             }
243             return null;
244         }
245         
246         public String identityHashCode()
247         {
248             if ( get() != null )
249             {
250                 return Integer.toHexString( System.identityHashCode( get() ) );
251             }
252             return null;
253         }
254         
255     }
256 }
257     
258 /*
259  * $Log$
260  * Revision 1.2  2006/02/18 23:19:05  cgruber
261  * Update imports and maven dependencies.
262  *
263  * Revision 1.1  2006/02/16 13:22:22  cgruber
264  * Check in all sources in eclipse-friendly maven-enabled packages.
265  *
266  * Revision 1.5  2003/08/06 23:07:52  chochos
267  * general code cleanup (mostly, removing unused imports)
268  *
269  * Revision 1.4  2002/03/22 22:39:59  mpowers
270  * Now shows the window even if previously hidden.
271  *
272  * Revision 1.3  2001/10/02 14:22:39  mpowers
273  * Now shows used and heap memory usage.
274  *
275  * Revision 1.2  2001/07/10 16:39:32  mpowers
276  * Removed printlns.
277  *
278  * Revision 1.1  2001/07/10 16:32:50  mpowers
279  * Adding the reference inspector.
280  *
281  *
282  */
283