View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2009 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  
88  			public void componentResized( ComponentEvent e )
89  			{
90  				initScales();
91  			}
92  		} );
93  	}
94  
95  	public TableModel getModel()
96  	{
97  		return data;
98  	}
99  
100 	public void release()
101 	{
102 		data.removeTableModelListener( tableModelListener );
103 	}
104 
105 	public void setTestStep( TestStep testStep )
106 	{
107 		if( data != null )
108 		{
109 			data.removeTableModelListener( tableModelListener );
110 			data.release();
111 		}
112 
113 		if( testStep == null )
114 		{
115 			data = statisticsModel.getHistory().getTestStepHistory( LoadTestStatistics.TOTAL );
116 		}
117 		else
118 		{
119 			data = statisticsModel.getHistory().getTestStepHistory( testStep.getTestCase().getIndexOfTestStep( testStep ) );
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() )
170 				/ ( float )( maxValues[index] + 10 );
171 		if( scale > 1 )
172 			scale = 1;
173 
174 		if( Float.compare( scale, scales[index] ) == 0 )
175 		{
176 			return false;
177 		}
178 
179 		scales[index] = scale;
180 		return true;
181 	}
182 
183 	public void paintComponent( Graphics g )
184 	{
185 		g.setColor( getBackground() );
186 
187 		Rectangle clip = g.getClipBounds();
188 		g.fillRect( ( int )clip.getX(), ( int )clip.getY(), ( int )clip.getWidth(), ( int )clip.getHeight() );
189 
190 		double right = clip.getX() + clip.getWidth();
191 		int rowCount = data.getRowCount();
192 		int height = getHeight();
193 
194 		for( int c = ( int )clip.getX(); c < rowCount && c < right; c++ )
195 		{
196 			for( int i = 0; i < data.getColumnCount(); i++ )
197 			{
198 				if( i == 0 )
199 					g.setColor( THREADCOUNT_COLOR );
200 				else if( i == Statistic.AVERAGE.getIndex() + 1 )
201 					g.setColor( AVERAGE_COLOR );
202 				else if( i == Statistic.ERRORS.getIndex() + 1 )
203 					g.setColor( ERRORS_COLOR );
204 				else if( i == Statistic.TPS.getIndex() + 1 )
205 					g.setColor( TPS_COLOR );
206 				else if( i == Statistic.BPS.getIndex() + 1 )
207 					g.setColor( BPS_COLOR );
208 				else
209 					continue;
210 
211 				int yOffset = ( int )( ( float )( ( Long )data.getValueAt( c, i ) ) * scales[i] );
212 
213 				if( clip.contains( c, height - yOffset - 1 ) )
214 				{
215 					g.drawLine( c, height - yOffset - 1, c, height - yOffset - 1 );
216 				}
217 			}
218 		}
219 	}
220 
221 	public JComponent getLegend()
222 	{
223 		if( legend == null )
224 			buildLegend();
225 
226 		return legend;
227 	}
228 
229 	private void buildLegend()
230 	{
231 		ButtonBarBuilder builder = new ButtonBarBuilder();
232 
233 		builder.addFixed( new JLabel( "ThreadCount", createLegendIcon( THREADCOUNT_COLOR ), JLabel.LEFT ) );
234 		builder.addUnrelatedGap();
235 		builder.addFixed( new JLabel( "Average (ms)", createLegendIcon( AVERAGE_COLOR ), JLabel.LEFT ) );
236 		builder.addUnrelatedGap();
237 		builder.addFixed( new JLabel( "ErrorCount", createLegendIcon( ERRORS_COLOR ), JLabel.LEFT ) );
238 		builder.addUnrelatedGap();
239 		builder.addFixed( new JLabel( "Transaction/Sec", createLegendIcon( TPS_COLOR ), JLabel.LEFT ) );
240 		builder.addUnrelatedGap();
241 		builder.addFixed( new JLabel( "Bytes/Sec", createLegendIcon( BPS_COLOR ), JLabel.LEFT ) );
242 
243 		builder.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
244 
245 		legend = builder.getPanel();
246 	}
247 
248 	private Icon createLegendIcon( Color color )
249 	{
250 		BufferedImage image = new BufferedImage( 10, 10, BufferedImage.TYPE_3BYTE_BGR );
251 		Graphics g = image.getGraphics();
252 		g.setColor( color );
253 		g.fillRect( 1, 1, 8, 8 );
254 		g.setColor( Color.DARK_GRAY );
255 		g.drawRect( 0, 0, 10, 10 );
256 		return new ImageIcon( image );
257 	}
258 
259 	public Dimension getPreferredScrollableViewportSize()
260 	{
261 		return getPreferredSize();
262 	}
263 
264 	public Dimension getPreferredSize()
265 	{
266 		int height = getHeight();
267 		int width = data.getRowCount() + SCROLL_AHEAD;
268 		return new Dimension( width, height );
269 	}
270 
271 	public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction )
272 	{
273 		return 1;
274 	}
275 
276 	public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction )
277 	{
278 		return 10;
279 	}
280 
281 	public boolean getScrollableTracksViewportWidth()
282 	{
283 		return false;
284 	}
285 
286 	public boolean getScrollableTracksViewportHeight()
287 	{
288 		return true;
289 	}
290 
291 	private final class InternalTableModelListener implements TableModelListener
292 	{
293 		public synchronized void tableChanged( TableModelEvent e )
294 		{
295 			boolean repaint = false;
296 
297 			if( e.getType() == TableModelEvent.INSERT )
298 			{
299 				int firstRow = e.getFirstRow();
300 				int lastRow = e.getLastRow();
301 				int height = getHeight();
302 
303 				for( int c = firstRow; c <= lastRow; c++ )
304 				{
305 					for( int i = 0; i < data.getColumnCount(); i++ )
306 					{
307 						long value = ( Long )data.getValueAt( c, i );
308 
309 						if( value > maxValues[i] )
310 						{
311 							maxValues[i] = value;
312 							repaint = recalcScale( i );
313 						}
314 					}
315 				}
316 
317 				if( !repaint )
318 				{
319 					Rectangle rect = new Rectangle( firstRow, 0, ( lastRow - firstRow ) + 1, height );
320 					repaint( rect );
321 				}
322 
323 				Dimension size = getSize();
324 				Rectangle r = getVisibleRect();
325 
326 				double x2 = r.getX() + r.getWidth();
327 				if( x2 >= data.getRowCount() && x2 < data.getRowCount() + SCROLL_AHEAD )
328 				{
329 					scrollRectToVisible( new Rectangle( firstRow + SCROLL_AHEAD / 2, 0, ( lastRow - firstRow ) + 1, height ) );
330 				}
331 
332 				if( !repaint && size.getWidth() < data.getRowCount() + SCROLL_AHEAD )
333 				{
334 					revalidate();
335 				}
336 			}
337 			else if( e.getType() == TableModelEvent.UPDATE )
338 			{
339 				initMaxValues();
340 				initScales();
341 
342 				repaint = true;
343 			}
344 
345 			if( repaint )
346 			{
347 				getParent().invalidate();
348 				revalidate();
349 				repaint();
350 			}
351 		}
352 	}
353 
354 	private class InternalMouseMotionListener implements MouseMotionListener
355 	{
356 		public void mouseDragged( MouseEvent e )
357 		{
358 			Rectangle r = new Rectangle( e.getX(), e.getY(), 1, 1 );
359 			scrollRectToVisible( r );
360 		}
361 
362 		public void mouseMoved( MouseEvent e )
363 		{
364 		}
365 	}
366 }