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  	private final WsdlLoadTest loadTest;
60  	private final LoadTestStatistics statisticsModel;
61  	private StatisticsHistoryModel data;
62  	private JComponent legend;
63  	private InternalTableModelListener tableModelListener = new InternalTableModelListener();
64  	private long [] maxValues;
65  	private float [] scales;
66  
67  	public JStatisticsGraph(WsdlLoadTest loadTest)
68  	{
69  		this.loadTest = loadTest;
70  		this.statisticsModel = loadTest.getStatisticsModel();
71  		this.data = statisticsModel.getHistory().getTestStepHistory(LoadTestStatistics.TOTAL);
72  
73  		setAutoscrolls(true);
74  		addMouseMotionListener(new InternalMouseMotionListener());
75  
76  		data.addTableModelListener(tableModelListener);
77  		
78  		initMaxValues();
79  		initScales();
80  		
81  		setBackground( Color.WHITE );
82  		setOpaque( true );
83  		
84  		addComponentListener( new ComponentAdapter() {
85  
86  			public void componentResized(ComponentEvent e)
87  			{
88  				initScales();
89  			}
90  		} );
91  	}
92  
93  	public TableModel getModel()
94  	{
95  		return data;
96  	}
97  	
98  	@Override
99  	public void removeNotify()
100 	{
101 		super.removeNotify();
102 		data.removeTableModelListener( tableModelListener );
103 	}
104 	
105 	@Override
106 	public void addNotify()
107 	{
108 		super.addNotify();
109 		data.addTableModelListener( tableModelListener );
110 	}
111 	
112 	public void setTestStep(TestStep testStep)
113 	{
114 		if (data != null)
115 		{
116 			data.removeTableModelListener(tableModelListener);
117 			data.release();
118 		}
119 		
120 		if (testStep == null)
121 		{
122 			data = statisticsModel.getHistory().getTestStepHistory(LoadTestStatistics.TOTAL);
123 		}
124 		else
125 		{
126 			data = statisticsModel.getHistory().getTestStepHistory(testStep.getTestCase().getIndexOfTestStep(testStep));
127 		}
128 		
129 		
130 		initMaxValues();
131 		initScales();
132 		
133 		data.addTableModelListener(tableModelListener);
134 		
135 		getParent().invalidate();
136 		revalidate();
137 		repaint();
138 	}
139 	
140 	public long getResolution()
141 	{
142 		return statisticsModel.getHistory().getResolution();
143 	}
144 	
145 	public void setResolution( long resolution )
146 	{
147 		statisticsModel.getHistory().setResolution( resolution );
148 	}
149 
150 	private void initMaxValues()
151 	{
152 		maxValues = new long[data.getColumnCount()];
153 		
154 		for( int c = 0; c < data.getRowCount(); c++ )
155 		{
156 			for( int i = 0; i < data.getColumnCount(); i++ )
157 			{
158 				long value = (Long)data.getValueAt( c, i );
159 				if( value > maxValues[i] ) 
160 					maxValues[i] = value;
161 			}
162 		}
163 	}
164 
165 	private void initScales()
166 	{
167 		scales = new float[maxValues.length];
168 		
169 		for( int c = 0; c < maxValues.length; c++ )
170 		{
171 			recalcScale( c );
172 		}
173 	}
174 
175 	private boolean recalcScale( int index)
176 	{
177 		float scale = (index == 0 || maxValues[index] == 0) ? 1 : (float)(getHeight())/(float)(maxValues[index]+10);
178 		if( scale > 1 ) scale = 1;
179 		
180 		if( Float.compare( scale, scales[index] ) == 0 )
181 		{
182 			return false;
183 		}
184 		
185 		scales[index] = scale;
186 		return true;
187 	}
188 	
189 	public void paintComponent(Graphics g)
190 	{
191 		g.setColor( getBackground() );
192 		
193 		Rectangle clip = g.getClipBounds();
194 		g.fillRect( (int)clip.getX(), (int)clip.getY(), (int)clip.getWidth(), (int)clip.getHeight() );
195 		
196 		double right = clip.getX() + clip.getWidth();
197 		int rowCount = data.getRowCount();
198 		int height = getHeight();
199 		
200 		for( int c = (int) clip.getX(); c < rowCount && c < right; c++ )
201 		{
202 			for (int i = 0; i < data.getColumnCount(); i++)
203 			{
204 				if( i == 0 )
205 					g.setColor( THREADCOUNT_COLOR ); 
206 				else if( i == Statistic.AVERAGE.getIndex()+1 )
207 					g.setColor( AVERAGE_COLOR ); 
208 				else if( i == Statistic.ERRORS.getIndex()+1 )
209 					g.setColor( ERRORS_COLOR ); 
210 				else if( i == Statistic.TPS.getIndex()+1 )
211 					g.setColor( TPS_COLOR ); 
212 				else if( i == Statistic.LAST.getIndex()+1 )
213 					g.setColor( LAST_COLOR ); 
214 				else if( i == Statistic.BPS.getIndex()+1 )
215 					g.setColor( BPS_COLOR ); 
216 				else continue;
217 
218 				int yOffset = (int) ((float) ((Long) data.getValueAt(c, i)) * scales[i]);
219 
220 				if( clip.contains( c, height - yOffset - 1 ))
221 				{
222 					g.drawLine(c, height - yOffset - 1, c, height - yOffset -1);
223 				}
224 			}
225 		}
226 	}
227 
228 	public JComponent getLegend()
229 	{
230 		if (legend == null)
231 			buildLegend();
232 
233 		return legend;
234 	}
235 
236 	private void buildLegend()
237 	{
238 		ButtonBarBuilder builder = new ButtonBarBuilder();
239 
240 		builder.addFixed( new JLabel( "ThreadCount", createLegendIcon( THREADCOUNT_COLOR ), JLabel.LEFT ));
241 		builder.addUnrelatedGap();
242 		builder.addFixed( new JLabel( "Average (ms)", createLegendIcon( AVERAGE_COLOR ), JLabel.LEFT ));
243 		builder.addUnrelatedGap();
244 		builder.addFixed( new JLabel( "ErrorCount", createLegendIcon( ERRORS_COLOR ), JLabel.LEFT ));
245 		builder.addUnrelatedGap();
246 		builder.addFixed( new JLabel( "Transaction/Sec", createLegendIcon( TPS_COLOR ), JLabel.LEFT ));
247 		builder.addUnrelatedGap();
248 		builder.addFixed( new JLabel( "Bytes/Sec", createLegendIcon( BPS_COLOR ), JLabel.LEFT ));
249 		builder.addUnrelatedGap();
250 		builder.addFixed( new JLabel( "Last (ms)", createLegendIcon( LAST_COLOR ), JLabel.LEFT ));
251 
252 		builder.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ));
253 		
254 		legend = builder.getPanel();
255 	}
256 
257 	private Icon createLegendIcon(Color color)
258 	{
259 		BufferedImage image = new BufferedImage( 10, 10, BufferedImage.TYPE_3BYTE_BGR );
260 		Graphics g = image.getGraphics();
261 		g.setColor( color );
262 		g.fillRect( 1, 1, 8, 8 );
263 		g.setColor( Color.DARK_GRAY );
264 		g.drawRect( 0, 0, 10, 10 );
265 		return new ImageIcon( image );
266 	}
267 
268 	public Dimension getPreferredScrollableViewportSize()
269 	{
270 		return getPreferredSize();
271 	}
272 	
273 	public Dimension getPreferredSize()
274 	{
275 		int height = getHeight();
276 		int width = data.getRowCount() + SCROLL_AHEAD;
277 		return new Dimension( width, height);
278 	}
279 
280 	public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
281 	{
282 		return 1;
283 	}
284 
285 	public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
286 	{
287 		return 10;
288 	}
289 
290 	public boolean getScrollableTracksViewportWidth()
291 	{
292 		return false;
293 	}
294 
295 	public boolean getScrollableTracksViewportHeight()
296 	{
297 		return true;
298 	}
299 
300 	private final class InternalTableModelListener implements TableModelListener
301 	{
302 		public synchronized void tableChanged(TableModelEvent e)
303 		{
304 			boolean repaint = false;
305 			
306 			if( e.getType() == TableModelEvent.INSERT )
307 			{
308 				int firstRow = e.getFirstRow();
309 				int lastRow = e.getLastRow();
310 				int height = getHeight();
311 				
312 				for( int c = firstRow; c <= lastRow; c++ )
313 				{
314 					for( int i = 0; i < data.getColumnCount(); i++ )
315 					{
316 						long value = (Long)data.getValueAt( c, i );
317 						
318 						if( value > maxValues[i] ) 
319 						{
320 							maxValues[i] = value;
321 							repaint = recalcScale( i );
322 						}
323 					}
324 				}
325 				
326 				if( !repaint )
327 				{
328 					Rectangle rect = new Rectangle(firstRow, 0, (lastRow-firstRow)+1, height );
329 					repaint( rect );
330 				}
331 				
332 				Dimension size = getSize();
333 				Rectangle r = getVisibleRect();
334 				
335 				double x2 = r.getX() + r.getWidth();
336 				if( x2 >= data.getRowCount() && x2 < data.getRowCount()+SCROLL_AHEAD)
337 				{
338 					scrollRectToVisible( new Rectangle(firstRow + SCROLL_AHEAD/2, 0, (lastRow-firstRow)+1, height ) );
339 				}
340 				
341 				if( !repaint && size.getWidth() < data.getRowCount() + SCROLL_AHEAD )
342 				{
343 					revalidate();
344 				}
345 			}
346 			else if( e.getType() == TableModelEvent.UPDATE )
347 			{
348 				initMaxValues();
349 				initScales();
350 				
351 				repaint = true;
352 			}
353 			
354 			if( repaint )
355 			{
356 				getParent().invalidate();
357 				revalidate();
358 				repaint();
359 			}
360 		}
361 	}
362 
363 	private class InternalMouseMotionListener implements MouseMotionListener
364 	{
365 		public void mouseDragged(MouseEvent e)
366 		{
367 			Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
368 			scrollRectToVisible(r);
369 		}
370 
371 		public void mouseMoved(MouseEvent e)
372 		{
373 		}
374 	}
375 }