Author: max.andersen(a)jboss.com
Date: 2006-09-20 12:25:44 -0400 (Wed, 20 Sep 2006)
New Revision: 10498
Added:
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/AbstractTreeModel.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/BeanTableModel.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsBrowser.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsCellRenderer.java
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsTreeModel.java
Log:
HBX-759 Add a statistics browser that is easy to use from hibernate apps
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/AbstractTreeModel.java
===================================================================
---
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/AbstractTreeModel.java 2006-09-20
09:27:18 UTC (rev 10497)
+++
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/AbstractTreeModel.java 2006-09-20
16:25:44 UTC (rev 10498)
@@ -0,0 +1,158 @@
+package org.hibernate.tool.stat;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.event.EventListenerList;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+/**
+ * Abstract class implementing the support for TreeModelEvents
+ * and TreeModelListeners. The code is partly snipped from the
+ * implementation of DefaultTreeModel.
+ *
+ * @version $Revision: 1.1.2.1 $
+ **/
+
+public abstract class AbstractTreeModel implements TreeModel {
+
+ private EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Adds a listener for the TreeModelEvent posted after the tree changes.
+ *
+ * @see #removeTreeModelListener
+ * @param l the listener to add
+ */
+ public void addTreeModelListener(TreeModelListener l) {
+ listenerList.add(TreeModelListener.class, l);
+ }
+
+ /**
+ * Removes a listener previously added with
<B>addTreeModelListener()</B>.
+ *
+ * @see #addTreeModelListener
+ * @param l the listener to remove
+ */
+ public void removeTreeModelListener(TreeModelListener l) {
+ listenerList.remove(TreeModelListener.class, l);
+ }
+
+ /**
+ * Messaged when the user has altered the value for the item identified
+ * by <I>path</I> to <I>newValue</I>. If
<I>newValue</I> signifies
+ * a truly new value the model should post a treeNodesChanged
+ * event.
+ *
+ * @param path path to the node that the user has altered.
+ * @param newValue the new value from the TreeCellEditor.
+ */
+ public void valueForPathChanged(TreePath path, Object newValue) {
+ throw new RuntimeException("AbstractTreeModel.valueForPathChanged: you MUST
override this when using a TreeCellEditor!");
+ }
+
+ /*
+ * Notify all listeners that have registered interest for
+ * notification on this event type. The event instance
+ * is lazily created using the parameters passed into
+ * the fire method.
+ * @see EventListenerList
+ */
+ protected void fireTreeNodesChanged(Object source, Object[] path,
+ int[] childIndices,
+ Object[] children) {
+ // Guaranteed to return a non-null array
+ Object[] listeners = listenerList.getListenerList();
+ TreeModelEvent e = null;
+ // Process the listeners last to first, notifying
+ // those that are interested in this event
+ for (int i = listeners.length-2; i>=0; i-=2) {
+ if (listeners[i]==TreeModelListener.class) {
+ // Lazily create the event:
+ if (e == null)
+ e = new TreeModelEvent(source, path,
+ childIndices, children);
+ ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
+ }
+ }
+ }
+
+ /*
+ * Notify all listeners that have registered interest for
+ * notification on this event type. The event instance
+ * is lazily created using the parameters passed into
+ * the fire method.
+ * @see EventListenerList
+ */
+ protected void fireTreeNodesInserted(Object source, Object[] path,
+ int[] childIndices,
+ Object[] children) {
+ // Guaranteed to return a non-null array
+ Object[] listeners = listenerList.getListenerList();
+ TreeModelEvent e = null;
+ // Process the listeners last to first, notifying
+ // those that are interested in this event
+ for (int i = listeners.length-2; i>=0; i-=2) {
+ if (listeners[i]==TreeModelListener.class) {
+ // Lazily create the event:
+ if (e == null)
+ e = new TreeModelEvent(source, path,
+ childIndices, children);
+ ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
+ }
+ }
+ }
+
+ /*
+ * Notify all listeners that have registered interest for
+ * notification on this event type. The event instance
+ * is lazily created using the parameters passed into
+ * the fire method.
+ * @see EventListenerList
+ */
+ protected void fireTreeNodesRemoved(Object source, Object[] path,
+ int[] childIndices,
+ Object[] children) {
+ // Guaranteed to return a non-null array
+ Object[] listeners = listenerList.getListenerList();
+ TreeModelEvent e = null;
+ // Process the listeners last to first, notifying
+ // those that are interested in this event
+ for (int i = listeners.length-2; i>=0; i-=2) {
+ if (listeners[i]==TreeModelListener.class) {
+ // Lazily create the event:
+ if (e == null)
+ e = new TreeModelEvent(source, path,
+ childIndices, children);
+ ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
+ }
+ }
+ }
+
+ /*
+ * Notify all listeners that have registered interest for
+ * notification on this event type. The event instance
+ * is lazily created using the parameters passed into
+ * the fire method.
+ * @see EventListenerList
+ */
+ protected void fireTreeStructureChanged(Object source, Object[] path,
+ int[] childIndices,
+ Object[] children) {
+ // Guaranteed to return a non-null array
+ Object[] listeners = listenerList.getListenerList();
+ TreeModelEvent e = null;
+ // Process the listeners last to first, notifying
+ // those that are interested in this event
+ for (int i = listeners.length-2; i>=0; i-=2) {
+ if (listeners[i]==TreeModelListener.class) {
+ // Lazily create the event:
+ if (e == null)
+ e = new TreeModelEvent(source, path,
+ childIndices, children);
+ ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
+ }
+ }
+ }
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/BeanTableModel.java
===================================================================
---
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/BeanTableModel.java 2006-09-20
09:27:18 UTC (rev 10497)
+++
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/BeanTableModel.java 2006-09-20
16:25:44 UTC (rev 10498)
@@ -0,0 +1,112 @@
+package org.hibernate.tool.stat;
+
+import java.beans.BeanDescriptor;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+public class BeanTableModel extends AbstractTableModel {
+
+ protected List list;
+
+ private BeanInfo beanInfo = null;
+
+ private PropertyDescriptor[] descriptors = null;
+
+ public BeanTableModel(List list, Class beanClass) {
+ this.list = list;
+ introspect( beanClass );
+ }
+
+ private void introspect(Class beanClass) {
+ try {
+ this.beanInfo = Introspector.getBeanInfo( beanClass,
+ Introspector.USE_ALL_BEANINFO );
+ //descriptor = beanInfo.getBeanDescriptor();
+ descriptors = beanInfo.getPropertyDescriptors();
+ }
+ catch (IntrospectionException ie) {
+ // ignore
+ }
+
+ List v = new ArrayList(descriptors.length);
+ for (int i = 0; i < descriptors.length; i++) {
+ if(!descriptors[i].getName().equals("class")) {
+ v.add( descriptors[i] );
+ }
+ }
+ descriptors = (PropertyDescriptor[]) v.toArray( new PropertyDescriptor[v.size()] );
+
+ }
+
+ boolean isSingle() {
+ return list.size()<=1;
+ }
+
+ public int getRowCount() {
+ return isSingle() ? descriptors.length : list.size();
+ }
+
+ public int getColumnCount() {
+ return isSingle() ? list.size() + 1 : (descriptors != null ? descriptors.length : 0);
+ }
+
+ public Object getValueAt(int row, int col) {
+ if(isSingle()) {
+ if(col==0) {
+ return descriptors[row].getDisplayName();
+ } else {
+ return getValue(0, row);
+ }
+ } else {
+ return getValue( row, col );
+ }
+ }
+
+ private Object getValue(int row, int col) {
+ Object bean = list.get( row );
+ Object result = null;
+ try {
+ result = descriptors[col].getReadMethod().invoke( bean, null );
+ }
+ catch (InvocationTargetException ite) {
+ ite.printStackTrace();
+ }
+ catch (IllegalAccessException iae) {
+ iae.printStackTrace();
+ }
+ return result;
+ }
+
+ public String getColumnName(int col) {
+ if(isSingle()) {
+ if(col==0) {
+ return "Name";
+ } else {
+ return "Value";
+ }
+ } else {
+ return descriptors[col].getDisplayName();
+ }
+ }
+
+ public Class getColumnClass(int c) {
+ if(isSingle()) {
+ return String.class;
+ } else {
+ Class propertyType = descriptors[c].getPropertyType();
+
+ if(propertyType.isPrimitive()) {
+ return String.class; // to avoid jtable complain about null table renderer.
+ } else {
+ return propertyType;
+ }
+ }
+ }
+}
\ No newline at end of file
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsBrowser.java
===================================================================
---
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsBrowser.java 2006-09-20
09:27:18 UTC (rev 10497)
+++
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsBrowser.java 2006-09-20
16:25:44 UTC (rev 10498)
@@ -0,0 +1,102 @@
+package org.hibernate.tool.stat;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.JTree;
+import javax.swing.ToolTipManager;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+
+import org.hibernate.stat.Statistics;
+import org.hibernate.stat.ui.BeanTableModel;
+import org.hibernate.stat.ui.StatisticsCellRenderer;
+import org.hibernate.stat.ui.StatisticsTreeModel;
+
+/**
+ * Very rudimentary statistics browser.
+ *
+ * Usage:
+ * new StatisticsBrowser().showStatistics(getSessions().getStatistics(), shouldBlock);
+ *
+ * @author max
+ *
+ */
+public class StatisticsBrowser {
+
+ /**
+ *
+ * @param stats a Statistics instance obtained from a SessionFactory
+ * @param shouldBlock decides if the ui will be modal or not.
+ */
+ public void showStatistics(Statistics stats, boolean shouldBlock) {
+
+ JDialog main = new JDialog((JFrame)null, "Statistics browser");
+
+ main.getContentPane().setLayout(new BorderLayout());
+
+ final StatisticsTreeModel statisticsTreeModel = new StatisticsTreeModel(stats);
+ JTree tree = new JTree(statisticsTreeModel);
+ tree.setCellRenderer( new StatisticsCellRenderer() );
+ ToolTipManager.sharedInstance().registerComponent(tree);
+
+ JScrollPane treePane = new JScrollPane(tree);
+
+ final JTable table = new JTable();
+
+ JScrollPane tablePane = new JScrollPane(table);
+ tablePane.getViewport().setBackground( table.getBackground() );
+ final BeanTableModel beanTableModel = new BeanTableModel(Collections.EMPTY_LIST,
Object.class);
+ table.setModel( beanTableModel );
+
+ JSplitPane pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treePane, tablePane);
+ pane.setContinuousLayout( true );
+
+ main.getContentPane().add(pane, BorderLayout.CENTER);
+
+ tree.addTreeSelectionListener( new TreeSelectionListener() {
+
+ public void valueChanged(TreeSelectionEvent e) {
+ Object lastPathComponent = e.getPath().getLastPathComponent();
+ List l = new ArrayList();
+ if(statisticsTreeModel.isContainer( lastPathComponent )) {
+ int childCount = statisticsTreeModel.getChildCount( lastPathComponent );
+
+ Class cl = Object.class;
+ for (int i = 0; i < childCount; i++) {
+ Object v = statisticsTreeModel.getChild( lastPathComponent, i );
+ if(v!=null) cl = v.getClass();
+ l.add(v);
+ }
+ table.setModel( new BeanTableModel(l, cl) );
+ } else {
+ l.add( lastPathComponent );
+ table.setModel( new BeanTableModel(l, lastPathComponent.getClass()) );
+ }
+
+ //table.doLayout();
+
+ }
+
+ } );
+
+
+ main.getContentPane().setSize(new Dimension(640,480));
+ main.pack();
+ main.setModal(shouldBlock);
+ main.setVisible(true);
+ }
+
+
+
+
+
+}
Added:
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsCellRenderer.java
===================================================================
---
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsCellRenderer.java 2006-09-20
09:27:18 UTC (rev 10497)
+++
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsCellRenderer.java 2006-09-20
16:25:44 UTC (rev 10498)
@@ -0,0 +1,61 @@
+package org.hibernate.tool.stat;
+
+import java.awt.Component;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.swing.JLabel;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+import org.hibernate.stat.CategorizedStatistics;
+import org.hibernate.stat.CollectionStatistics;
+import org.hibernate.stat.EntityStatistics;
+import org.hibernate.stat.QueryStatistics;
+import org.hibernate.stat.Statistics;
+
+public class StatisticsCellRenderer extends DefaultTreeCellRenderer {
+
+ SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
+
+ public Component getTreeCellRendererComponent(JTree tree, Object value,
+ boolean selected, boolean expanded, boolean leaf, int row,
+ boolean hasFocus) {
+
+ JLabel treeCellRendererComponent = (JLabel) super.getTreeCellRendererComponent( tree,
value, selected, expanded, leaf, row, hasFocus );
+
+ String text = treeCellRendererComponent.getText();
+ String tooltip = null;
+ if(value instanceof Statistics) {
+ Statistics stats = (Statistics) value;
+ text = "Statistics " + formatter.format( new Date(stats.getStartTime()) );
+ tooltip = stats.toString();
+ }
+
+ if(value instanceof CategorizedStatistics) {
+ CategorizedStatistics stats = (CategorizedStatistics) value;
+ text = stats.getCategoryName();
+
+ }
+ if(value instanceof EntityStatistics) {
+ EntityStatistics stats = (EntityStatistics) value;
+
+ }
+
+ if(value instanceof CollectionStatistics) {
+ CollectionStatistics stats = (CollectionStatistics) value;
+
+ }
+
+ if(value instanceof QueryStatistics) {
+ QueryStatistics stats = (QueryStatistics) value;
+
+ }
+
+ treeCellRendererComponent.setText( text );
+ treeCellRendererComponent.setToolTipText( tooltip );
+ return treeCellRendererComponent;
+ }
+
+
+}
Added: trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsTreeModel.java
===================================================================
---
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsTreeModel.java 2006-09-20
09:27:18 UTC (rev 10497)
+++
trunk/HibernateExt/tools/src/java/org/hibernate/tool/stat/StatisticsTreeModel.java 2006-09-20
16:25:44 UTC (rev 10498)
@@ -0,0 +1,79 @@
+package org.hibernate.tool.stat;
+
+import org.hibernate.stat.Statistics;
+import org.hibernate.stat.ui.AbstractTreeModel;
+
+public class StatisticsTreeModel extends AbstractTreeModel {
+
+ private final Statistics stats;
+
+ String queries = "Queries";
+ String entities = "Entities";
+ String collections = "Collections";
+
+
+
+ public StatisticsTreeModel(Statistics stats) {
+ this.stats = stats;
+ }
+
+ public Object getChild(Object parent, int index) {
+ if(parent==stats) {
+ switch(index) {
+ case 0: return entities;
+ case 1: return collections;
+ case 2: return queries;
+ }
+ } else if(parent==entities) {
+ return stats.getEntityStatistics(stats.getEntityNames()[index]);
+ } else if(parent==collections) {
+ return stats.getCollectionStatistics(stats.getCollectionRoleNames()[index]);
+ } else if(parent==queries) {
+ return stats.getQueryStatistics(stats.getQueries()[index]);
+ }
+ return null;
+ }
+
+ public int getChildCount(Object parent) {
+ if(parent==stats) {
+ return 3;
+ } else if(parent==entities) {
+ return stats.getEntityNames().length;
+ } else if(parent==collections) {
+ return stats.getCollectionRoleNames().length;
+ } else if(parent==queries) {
+ return stats.getQueries().length;
+ }
+ return 0;
+ }
+
+ public int getIndexOfChild(Object parent, Object child) {
+ throw new IllegalAccessError();
+ //return 0;
+ }
+
+ public Object getRoot() {
+ return stats;
+ }
+
+ public boolean isLeaf(Object node) {
+ return false;
+ }
+
+ public boolean isQueries(Object o) {
+ return o==queries; // hack
+ }
+
+ public boolean isCollections(Object o) {
+ return o==collections; // hack
+ }
+
+ public boolean isEntities(Object o) {
+ return o==entities; // hack
+ }
+
+ public boolean isContainer(Object o) {
+ return isEntities( o ) || isQueries( o ) || isCollections( o );
+ }
+
+}
\ No newline at end of file