1
2
3
4
5
6
7
8
9
10
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 }