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 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 }