View Javadoc

    1   /*
    2    *  soapUI, copyright (C) 2004-2007 eviware.com
    3    *
    4    *  soapUI is free software; you can redistribute it and/or modify it under the
    5    *  terms of version 2.1 of the GNU Lesser General Public License as published by
    6    *  the Free Software Foundation.
    7    *
    8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
    9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    10   *  See the GNU Lesser General Public License for more details at gnu.org.
    11   */
    12
    13  package com.eviware.soapui.impl.wsdl.panels.loadtest;
    14
    15  import java.awt.Color;
    16  import java.awt.Dimension;
    17  import java.awt.Graphics;
    18  import java.awt.Rectangle;
    19  import java.awt.event.ComponentAdapter;
    20  import java.awt.event.ComponentEvent;
    21  import java.awt.event.MouseEvent;
    22  import java.awt.event.MouseMotionListener;
    23  import java.awt.image.BufferedImage;
    24
    25  import javax.swing.BorderFactory;
    26  import javax.swing.Icon;
    27  import javax.swing.ImageIcon;
    28  import javax.swing.JComponent;
    29  import javax.swing.JLabel;
    30  import javax.swing.Scrollable;
    31  import javax.swing.event.TableModelEvent;
    32  import javax.swing.event.TableModelListener;
    33  import javax.swing.table.TableModel;
    34
    35  import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
    36  import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics;
    37  import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics.Statistic;
    38  import com.eviware.soapui.impl.wsdl.loadtest.data.StatisticsHistory.StatisticsHistoryModel;
    39  import com.eviware.soapui.model.testsuite.TestStep;
    40  import com.jgoodies.forms.builder.ButtonBarBuilder;
    41
    42  /***
    43   * Graphical representation of testschedule statistics
    44   *
    45   * @author Ole.Matzura
    46   */
    47
    48  public class JStatisticsGraph extends JComponent implements Scrollable
    49  {
    50  	private static final Color THREADCOUNT_COLOR = Color.GREEN.darker();
    51  	private static final Color AVERAGE_COLOR = Color.BLUE;
    52  	private static final Color ERRORS_COLOR = Color.RED.darker();
    53  	private static final Color TPS_COLOR = Color.BLACK;
    54  	private static final Color BPS_COLOR = Color.ORANGE;
    55  	private static final Color LAST_COLOR = Color.MAGENTA.brighter();
    56
    57  	private static final int SCROLL_AHEAD = 50;
    58
    59  	@SuppressWarnings("unused")
    60  	private final WsdlLoadTest loadTest;
    61  	private final LoadTestStatistics statisticsModel;
    62  	private StatisticsHistoryModel data;
    63  	private JComponent legend;
    64  	private InternalTableModelListener tableModelListener = new InternalTableModelListener();
    65  	private long [] maxValues;
    66  	private float [] scales;
    67
    68  	public JStatisticsGraph(WsdlLoadTest loadTest)
    69  	{
    70  		this.loadTest = loadTest;
    71  		this.statisticsModel = loadTest.getStatisticsModel();
    72  		this.data = statisticsModel.getHistory().getTestStepHistory(LoadTestStatistics.TOTAL);
    73
    74  		setAutoscrolls(true);
    75  		addMouseMotionListener(new InternalMouseMotionListener());
    76
    77  		data.addTableModelListener(tableModelListener);
    78
    79  		initMaxValues();
    80  		initScales();
    81
    82  		setBackground( Color.WHITE );
    83  		setOpaque( true );
    84
    85  		addComponentListener( new ComponentAdapter() {
    86
    87  			public void componentResized(ComponentEvent e)
    88  			{
    89  				initScales();
    90  			}
    91  		} );
    92  	}
    93
    94  	public TableModel getModel()
    95  	{
    96  		return data;
    97  	}
    98
    99  	public void release()
    100 	{
    101 		data.removeTableModelListener( tableModelListener );
    102 	}
    103
    104 	public void setTestStep(TestStep testStep)
    105 	{
    106 		if (data != null)
    107 		{
    108 			data.removeTableModelListener(tableModelListener);
    109 			data.release();
    110 		}
    111
    112 		if (testStep == null)
    113 		{
    114 			data = statisticsModel.getHistory().getTestStepHistory(LoadTestStatistics.TOTAL);
    115 		}
    116 		else
    117 		{
    118 			data = statisticsModel.getHistory().getTestStepHistory(testStep.getTestCase().getIndexOfTestStep(testStep));
    119 		}
    120
    121
    122 		initMaxValues();
    123 		initScales();
    124
    125 		data.addTableModelListener(tableModelListener);
    126
    127 		getParent().invalidate();
    128 		revalidate();
    129 		repaint();
    130 	}
    131
    132 	public long getResolution()
    133 	{
    134 		return statisticsModel.getHistory().getResolution();
    135 	}
    136
    137 	public void setResolution( long resolution )
    138 	{
    139 		statisticsModel.getHistory().setResolution( resolution );
    140 	}
    141
    142 	private void initMaxValues()
    143 	{
    144 		maxValues = new long[data.getColumnCount()];
    145
    146 		for( int c = 0; c < data.getRowCount(); c++ )
    147 		{
    148 			for( int i = 0; i < data.getColumnCount(); i++ )
    149 			{
    150 				long value = (Long)data.getValueAt( c, i );
    151 				if( value > maxValues[i] )
    152 					maxValues[i] = value;
    153 			}
    154 		}
    155 	}
    156
    157 	private void initScales()
    158 	{
    159 		scales = new float[maxValues.length];
    160
    161 		for( int c = 0; c < maxValues.length; c++ )
    162 		{
    163 			recalcScale( c );
    164 		}
    165 	}
    166
    167 	private boolean recalcScale( int index)
    168 	{
    169 		float scale = (index == 0 || maxValues[index] == 0) ? 1 : (float)(getHeight())/(float)(maxValues[index]+10);
    170 		if( scale > 1 ) scale = 1;
    171
    172 		if( Float.compare( scale, scales[index] ) == 0 )
    173 		{
    174 			return false;
    175 		}
    176
    177 		scales[index] = scale;
    178 		return true;
    179 	}
    180
    181 	public void paintComponent(Graphics g)
    182 	{
    183 		g.setColor( getBackground() );
    184
    185 		Rectangle clip = g.getClipBounds();
    186 		g.fillRect( (int)clip.getX(), (int)clip.getY(), (int)clip.getWidth(), (int)clip.getHeight() );
    187
    188 		double right = clip.getX() + clip.getWidth();
    189 		int rowCount = data.getRowCount();
    190 		int height = getHeight();
    191
    192 		for( int c = (int) clip.getX(); c < rowCount && c < right; c++ )
    193 		{
    194 			for (int i = 0; i < data.getColumnCount(); i++)
    195 			{
    196 				if( i == 0 )
    197 					g.setColor( THREADCOUNT_COLOR );
    198 				else if( i == Statistic.AVERAGE.getIndex()+1 )
    199 					g.setColor( AVERAGE_COLOR );
    200 				else if( i == Statistic.ERRORS.getIndex()+1 )
    201 					g.setColor( ERRORS_COLOR );
    202 				else if( i == Statistic.TPS.getIndex()+1 )
    203 					g.setColor( TPS_COLOR );
    204 				else if( i == Statistic.LAST.getIndex()+1 )
    205 					g.setColor( LAST_COLOR );
    206 				else if( i == Statistic.BPS.getIndex()+1 )
    207 					g.setColor( BPS_COLOR );
    208 				else continue;
    209
    210 				int yOffset = (int) ((float) ((Long) data.getValueAt(c, i)) * scales[i]);
    211
    212 				if( clip.contains( c, height - yOffset - 1 ))
    213 				{
    214 					g.drawLine(c, height - yOffset - 1, c, height - yOffset -1);
    215 				}
    216 			}
    217 		}
    218 	}
    219
    220 	public JComponent getLegend()
    221 	{
    222 		if (legend == null)
    223 			buildLegend();
    224
    225 		return legend;
    226 	}
    227
    228 	private void buildLegend()
    229 	{
    230 		ButtonBarBuilder builder = new ButtonBarBuilder();
    231
    232 		builder.addFixed( new JLabel( "ThreadCount", createLegendIcon( THREADCOUNT_COLOR ), JLabel.LEFT ));
    233 		builder.addUnrelatedGap();
    234 		builder.addFixed( new JLabel( "Average (ms)", createLegendIcon( AVERAGE_COLOR ), JLabel.LEFT ));
    235 		builder.addUnrelatedGap();
    236 		builder.addFixed( new JLabel( "ErrorCount", createLegendIcon( ERRORS_COLOR ), JLabel.LEFT ));
    237 		builder.addUnrelatedGap();
    238 		builder.addFixed( new JLabel( "Transaction/Sec", createLegendIcon( TPS_COLOR ), JLabel.LEFT ));
    239 		builder.addUnrelatedGap();
    240 		builder.addFixed( new JLabel( "Bytes/Sec", createLegendIcon( BPS_COLOR ), JLabel.LEFT ));
    241 		builder.addUnrelatedGap();
    242 		builder.addFixed( new JLabel( "Last (ms)", createLegendIcon( LAST_COLOR ), JLabel.LEFT ));
    243
    244 		builder.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ));
    245
    246 		legend = builder.getPanel();
    247 	}
    248
    249 	private Icon createLegendIcon(Color color)
    250 	{
    251 		BufferedImage image = new BufferedImage( 10, 10, BufferedImage.TYPE_3BYTE_BGR );
    252 		Graphics g = image.getGraphics();
    253 		g.setColor( color );
    254 		g.fillRect( 1, 1, 8, 8 );
    255 		g.setColor( Color.DARK_GRAY );
    256 		g.drawRect( 0, 0, 10, 10 );
    257 		return new ImageIcon( image );
    258 	}
    259
    260 	public Dimension getPreferredScrollableViewportSize()
    261 	{
    262 		return getPreferredSize();
    263 	}
    264
    265 	public Dimension getPreferredSize()
    266 	{
    267 		int height = getHeight();
    268 		int width = data.getRowCount() + SCROLL_AHEAD;
    269 		return new Dimension( width, height);
    270 	}
    271
    272 	public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
    273 	{
    274 		return 1;
    275 	}
    276
    277 	public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
    278 	{
    279 		return 10;
    280 	}
    281
    282 	public boolean getScrollableTracksViewportWidth()
    283 	{
    284 		return false;
    285 	}
    286
    287 	public boolean getScrollableTracksViewportHeight()
    288 	{
    289 		return true;
    290 	}
    291
    292 	private final class InternalTableModelListener implements TableModelListener
    293 	{
    294 		public synchronized void tableChanged(TableModelEvent e)
    295 		{
    296 			boolean repaint = false;
    297
    298 			if( e.getType() == TableModelEvent.INSERT )
    299 			{
    300 				int firstRow = e.getFirstRow();
    301 				int lastRow = e.getLastRow();
    302 				int height = getHeight();
    303
    304 				for( int c = firstRow; c <= lastRow; c++ )
    305 				{
    306 					for( int i = 0; i < data.getColumnCount(); i++ )
    307 					{
    308 						long value = (Long)data.getValueAt( c, i );
    309
    310 						if( value > maxValues[i] )
    311 						{
    312 							maxValues[i] = value;
    313 							repaint = recalcScale( i );
    314 						}
    315 					}
    316 				}
    317
    318 				if( !repaint )
    319 				{
    320 					Rectangle rect = new Rectangle(firstRow, 0, (lastRow-firstRow)+1, height );
    321 					repaint( rect );
    322 				}
    323
    324 				Dimension size = getSize();
    325 				Rectangle r = getVisibleRect();
    326
    327 				double x2 = r.getX() + r.getWidth();
    328 				if( x2 >= data.getRowCount() && x2 < data.getRowCount()+SCROLL_AHEAD)
    329 				{
    330 					scrollRectToVisible( new Rectangle(firstRow + SCROLL_AHEAD/2, 0, (lastRow-firstRow)+1, height ) );
    331 				}
    332
    333 				if( !repaint && size.getWidth() < data.getRowCount() + SCROLL_AHEAD )
    334 				{
    335 					revalidate();
    336 				}
    337 			}
    338 			else if( e.getType() == TableModelEvent.UPDATE )
    339 			{
    340 				initMaxValues();
    341 				initScales();
    342
    343 				repaint = true;
    344 			}
    345
    346 			if( repaint )
    347 			{
    348 				getParent().invalidate();
    349 				revalidate();
    350 				repaint();
    351 			}
    352 		}
    353 	}
    354
    355 	private class InternalMouseMotionListener implements MouseMotionListener
    356 	{
    357 		public void mouseDragged(MouseEvent e)
    358 		{
    359 			Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
    360 			scrollRectToVisible(r);
    361 		}
    362
    363 		public void mouseMoved(MouseEvent e)
    364 		{
    365 		}
    366 	}
    367 }