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