View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 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.loadtest.data;
14  
15  import java.util.ArrayList;
16  import java.util.EnumMap;
17  import java.util.HashMap;
18  import java.util.List;
19  import java.util.Map;
20  
21  import javax.swing.event.TableModelEvent;
22  import javax.swing.event.TableModelListener;
23  import javax.swing.table.AbstractTableModel;
24  
25  import org.apache.log4j.Logger;
26  
27  import com.eviware.soapui.SoapUI;
28  import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
29  import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics.Statistic;
30  import com.eviware.soapui.model.support.LoadTestRunListenerAdapter;
31  import com.eviware.soapui.model.testsuite.LoadTestRunContext;
32  import com.eviware.soapui.model.testsuite.LoadTestRunner;
33  
34  /***
35   * Collector of statistics to be exposed as TableModels
36   * 
37   * @author Ole.Matzura
38   */
39  
40  public class StatisticsHistory
41  {
42  	private final LoadTestStatistics statistics;
43  	private List<long[][]> data = new ArrayList<long[][]>();
44  	private List<Long> threadCounts = new ArrayList<Long>();
45  	private Map<Integer, TestStepStatisticsHistory> testStepStatisticHistories = new HashMap<Integer, TestStepStatisticsHistory>();
46  	private EnumMap<Statistic, StatisticsValueHistory> statisticsValueHistories = new EnumMap<Statistic, StatisticsValueHistory>(
47  			Statistic.class );
48  
49  	@SuppressWarnings( "unused" )
50  	private final static Logger logger = Logger.getLogger( StatisticsHistory.class );
51  	private long resolution = 0;
52  	private InternalTableModelListener internalTableModelListener = new InternalTableModelListener();
53  	private Updater updater = new Updater();
54  
55  	public StatisticsHistory( LoadTestStatistics statistics )
56  	{
57  		this.statistics = statistics;
58  
59  		statistics.addTableModelListener( internalTableModelListener );
60  		statistics.getLoadTest().addLoadTestRunListener( new LoadTestRunListenerAdapter()
61  		{
62  
63  			public void beforeLoadTest( LoadTestRunner loadTestRunner, LoadTestRunContext context )
64  			{
65  				if( resolution > 0 )
66  					new Thread( updater, StatisticsHistory.this.statistics.getLoadTest().getName()
67  							+ " StatisticsHistory Updater" ).start();
68  			}
69  		} );
70  	}
71  
72  	public Map<Integer, TestStepStatisticsHistory> getTestStepStatisticHistories()
73  	{
74  		return testStepStatisticHistories;
75  	}
76  
77  	public long getResolution()
78  	{
79  		return resolution;
80  	}
81  
82  	public void setResolution( long resolution )
83  	{
84  		long old = this.resolution;
85  		this.resolution = resolution;
86  
87  		if( resolution > 0 && old == 0 && statistics.getLoadTest().getHistoryLimit() != 0 )
88  		{
89  			new Thread( updater, statistics.getLoadTest().getName() + " StatisticsHistory Updater" ).start();
90  		}
91  	}
92  
93  	public int getRowCount()
94  	{
95  		return data.size();
96  	}
97  
98  	public long[][] getHistoryAt( int index )
99  	{
100 		return data.get( index );
101 	}
102 
103 	public long getThreadCountAt( int index )
104 	{
105 		return threadCounts.get( index );
106 	}
107 
108 	public StatisticsHistoryModel getTestStepHistory( int testStepIndex )
109 	{
110 		if( !testStepStatisticHistories.containsKey( testStepIndex ) )
111 		{
112 			testStepStatisticHistories.put( testStepIndex, new TestStepStatisticsHistory( testStepIndex ) );
113 		}
114 
115 		return testStepStatisticHistories.get( testStepIndex );
116 	}
117 
118 	public StatisticsHistoryModel getStatisticsValueHistory( Statistic statistic )
119 	{
120 		if( !statisticsValueHistories.containsKey( statistic ) )
121 		{
122 			statisticsValueHistories.put( statistic, new StatisticsValueHistory( statistic ) );
123 		}
124 
125 		return statisticsValueHistories.get( statistic );
126 	}
127 
128 	public void reset()
129 	{
130 		data.clear();
131 		threadCounts.clear();
132 
133 		for( StatisticsValueHistory history : statisticsValueHistories.values() )
134 		{
135 			history.fireTableDataChanged();
136 			history.fireTableStructureChanged();
137 		}
138 
139 		for( TestStepStatisticsHistory history : testStepStatisticHistories.values() )
140 		{
141 			history.fireTableDataChanged();
142 			history.fireTableStructureChanged();
143 		}
144 	}
145 
146 	private synchronized void updateHistory()
147 	{
148 		if( statistics.getStatistic( LoadTestStatistics.TOTAL, Statistic.COUNT ) == 0 )
149 		{
150 			reset();
151 		}
152 		else
153 		{
154 			int columnCount = statistics.getColumnCount();
155 			int rowCount = statistics.getRowCount();
156 
157 			long[][] values = new long[rowCount][columnCount - 2];
158 
159 			for( int c = 0; c < rowCount; c++ )
160 			{
161 				for( int i = 2; i < columnCount; i++ )
162 				{
163 					try
164 					{
165 						values[c][i - 2] = Long.parseLong( statistics.getValueAt( c, i ).toString() );
166 					}
167 					catch( NumberFormatException ex )
168 					{
169 						values[c][i - 2] = ( long )Float.parseFloat( statistics.getValueAt( c, i ).toString() );
170 					}
171 				}
172 			}
173 
174 			data.add( values );
175 			threadCounts.add( statistics.getLoadTest().getThreadCount() );
176 
177 			// notify!
178 			int sz = data.size() - 1;
179 			for( StatisticsValueHistory history : statisticsValueHistories.values() )
180 			{
181 				history.fireTableRowsInserted( sz, sz );
182 			}
183 
184 			for( TestStepStatisticsHistory history : testStepStatisticHistories.values() )
185 			{
186 				history.fireTableRowsInserted( sz, sz );
187 			}
188 		}
189 	}
190 
191 	public abstract class StatisticsHistoryModel extends AbstractTableModel
192 	{
193 		public abstract void release();
194 	}
195 
196 	public class TestStepStatisticsHistory extends StatisticsHistoryModel
197 	{
198 		private final int testStepIndex;
199 
200 		public TestStepStatisticsHistory( int testStepIndex )
201 		{
202 			this.testStepIndex = testStepIndex == -1 ? statistics.getRowCount() - 1 : testStepIndex;
203 		}
204 
205 		public int getTestStepIndex()
206 		{
207 			return testStepIndex;
208 		}
209 
210 		public int getRowCount()
211 		{
212 			return data.size();
213 		}
214 
215 		public int getColumnCount()
216 		{
217 			return statistics.getColumnCount() - 1;
218 		}
219 
220 		public Object getValueAt( int rowIndex, int columnIndex )
221 		{
222 			if( columnIndex == 0 )
223 				return threadCounts.get( rowIndex );
224 
225 			// tolerance..
226 			if( rowIndex < data.size() )
227 				return data.get( rowIndex )[testStepIndex][columnIndex - 1];
228 			else
229 				return new Long( 0 );
230 		}
231 
232 		public Class<?> getColumnClass( int columnIndex )
233 		{
234 			return Long.class;
235 		}
236 
237 		public String getColumnName( int column )
238 		{
239 			return column == 0 ? "ThreadCount" : Statistic.forIndex( column - 1 ).getName();
240 		}
241 
242 		public void release()
243 		{
244 			testStepStatisticHistories.remove( testStepIndex );
245 		}
246 	}
247 
248 	private class StatisticsValueHistory extends StatisticsHistoryModel
249 	{
250 		private final Statistic statistic;
251 
252 		public StatisticsValueHistory( Statistic statistic )
253 		{
254 			this.statistic = statistic;
255 		}
256 
257 		@SuppressWarnings( "unused" )
258 		public Statistic getStatistic()
259 		{
260 			return statistic;
261 		}
262 
263 		public int getRowCount()
264 		{
265 			return data.size();
266 		}
267 
268 		public int getColumnCount()
269 		{
270 			return statistics.getRowCount() + 1;
271 		}
272 
273 		public Object getValueAt( int rowIndex, int columnIndex )
274 		{
275 			if( columnIndex == 0 )
276 				return threadCounts.get( rowIndex );
277 
278 			return data.get( rowIndex )[columnIndex - 1][statistic.getIndex()];
279 		}
280 
281 		public Class<?> getColumnClass( int columnIndex )
282 		{
283 			return Long.class;
284 		}
285 
286 		public String getColumnName( int column )
287 		{
288 			if( column == 0 )
289 				return "ThreadCount";
290 
291 			if( column == statistics.getRowCount() )
292 				return "Total";
293 
294 			return statistics.getLoadTest().getTestCase().getTestStepAt( column - 1 ).getName();
295 		}
296 
297 		public void release()
298 		{
299 			statisticsValueHistories.remove( statistic );
300 		}
301 	}
302 
303 	private class InternalTableModelListener implements TableModelListener
304 	{
305 		public synchronized void tableChanged( TableModelEvent e )
306 		{
307 			if( ( resolution > 0 && statistics.getLoadTest().isRunning() ) || e.getType() != TableModelEvent.UPDATE
308 					|| statistics.getLoadTest().getHistoryLimit() == 0 )
309 				return;
310 
311 			updateHistory();
312 		}
313 	}
314 
315 	private final class Updater implements Runnable
316 	{
317 		public void run()
318 		{
319 			WsdlLoadTest loadTest = statistics.getLoadTest();
320 
321 			while( resolution > 0 && loadTest.isRunning() )
322 			{
323 				try
324 				{
325 					if( loadTest.getHistoryLimit() != 0 )
326 						updateHistory();
327 
328 					// chunck wait so we can get canceled..
329 					long res = resolution;
330 					while( res > 100 && resolution > 0 && loadTest.isRunning() )
331 					{
332 						Thread.sleep( res );
333 						res -= 100;
334 					}
335 
336 					if( resolution > 0 && loadTest.isRunning() )
337 						Thread.sleep( res );
338 				}
339 				catch( InterruptedException e )
340 				{
341 					SoapUI.logError( e );
342 					break;
343 				}
344 			}
345 		}
346 	}
347 }