package org.radargun.stats; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.radargun.Operation; import org.radargun.config.DefinitionElement; import org.radargun.config.Property; /** * Implements the Statistics interface using provided {@link OperationStats}. * * @author Radim Vansa <rvansa@redhat.com> */ @DefinitionElement(name = "basic", doc = "Statistics with fixed memory footprint for each operation statistics.") public class BasicStatistics extends IntervalStatistics { private static final OperationStats[] EMPTY_ARRAY = new OperationStats[0]; private transient OperationStats[] operationStats = EMPTY_ARRAY; private Map<String, OperationStats> operationStatsMap = new HashMap<>(); private Map<String, Set<Operation>> groupOperationsMap = new HashMap<>(); private Map<Operation, String> operationGroupMap = new HashMap<>(); @Property(name = "operationStats", doc = "Operation statistics prototype.", complexConverter = OperationStats.Converter.class) protected OperationStats prototype = new BasicOperationStats(); public BasicStatistics() { } public BasicStatistics(OperationStats prototype) { this.prototype = prototype; } protected OperationStats createOperationStats(int operationId) { return prototype.copy(); } public Statistics newInstance() { return new BasicStatistics(prototype); } @Override public void registerOperationsGroup(String name, Set<Operation> operations) { if (groupOperationsMap.containsKey(name)) { return; } for (Map.Entry<String, Set<Operation>> entry : groupOperationsMap.entrySet()) { for (Operation operation : operations) { if (entry.getValue().contains(operation)) { return; } } } groupOperationsMap.put(name, operations); for (Operation operation : operations) { ensure(operation.id); operationGroupMap.put(operation, name); } } @Override public void reset() { operationStatsMap.clear(); for (int i = 0; i < operationStats.length; ++i) { operationStats[i] = createOperationStats(i); operationStatsMap.put(Operation.getById(i).name, operationStats[i]); } begin(); } @Override public void record(Request request, Operation operation) { ensure(operation.id); OperationStats stats = operationStats[operation.id]; stats.record(request); } @Override public void record(Message message, Operation operation) { ensure(operation.id); OperationStats stats = operationStats[operation.id]; stats.record(message); } @Override public void record(RequestSet requestSet, Operation operation) { ensure(operation.id); OperationStats stats = operationStats[operation.id]; stats.record(requestSet); } private void ensure(int operationId) { if (operationStats == null) { operationStats = EMPTY_ARRAY; } if (operationId >= operationStats.length) { OperationStats[] temp = new OperationStats[operationId + 1]; System.arraycopy(operationStats, 0, temp, 0, operationStats.length); operationStats = temp; } if (operationStats[operationId] == null) { if (operationStatsMap.get(Operation.getById(operationId).name) == null) { OperationStats operationStats = createOperationStats(operationId); this.operationStats[operationId] = operationStats; operationStatsMap.put(Operation.getById(operationId).name, operationStats); } else { operationStats[operationId] = operationStatsMap.get(Operation.getById(operationId).name); } } } @Override public Statistics copy() { BasicStatistics copy = (BasicStatistics) newInstance(); copy.merge(this); return copy; } /** * Merge otherStats to this. leaves otherStats unchanged. * * @param otherStats */ @Override public void merge(Statistics otherStats) { if (!(otherStats instanceof BasicStatistics)) throw new IllegalArgumentException(otherStats.getClass().getName()); super.merge(otherStats); BasicStatistics stats = (BasicStatistics) otherStats; if (operationStats == null) { // after deserialization operationStats = EMPTY_ARRAY; } if (stats.operationStats == null) { for (Map.Entry<String, OperationStats> entry : stats.operationStatsMap.entrySet()) { Operation operation = Operation.getByName(entry.getKey()); ensure(operation.id); if (operationStats[operation.id] == null) { operationStats[operation.id] = entry.getValue().copy(); operationStatsMap.put(entry.getKey(), operationStats[operation.id]); } else { operationStats[operation.id].merge(entry.getValue()); } } } else { ensure(Math.max(0, stats.operationStats.length - 1)); for (int i = 0; i < stats.operationStats.length; ++i) { if (stats.operationStats[i] == null) { continue; } else if (operationStats[i] == null) { operationStats[i] = stats.operationStats[i].copy(); operationStatsMap.put(Operation.getById(i).name, operationStats[i]); } else { operationStats[i].merge(stats.operationStats[i]); } } } } @Override public List<Map<String, OperationStats>> getOperationStatsForGroups() { Map<String, OperationStats> result = new HashMap<>(groupOperationsMap.size()); for (Map.Entry<String, Set<Operation>> entry : groupOperationsMap.entrySet()) { OperationStats mergedOperationStats = null; for (Operation operation : entry.getValue()) { OperationStats os = operationStats[operation.id]; if (mergedOperationStats == null) { mergedOperationStats = os.copy(); } else { mergedOperationStats.merge(os); } } if (!mergedOperationStats.isEmpty()) { result.put(entry.getKey(), mergedOperationStats); } } List<Map<String, OperationStats>> list = new ArrayList<>(); list.add(result); return list; } @Override public List<Map<String, OperationStats>> getOperationsStats() { List<Map<String, OperationStats>> list = new ArrayList<>(); list.add(operationStatsMap); return list; } @Override public String getOperationsGroup(Operation operation) { return operationGroupMap.get(operation); } @Override public Map<String, Set<Operation>> getGroupOperationsMap() { return groupOperationsMap; } @Override public Set<String> getOperations() { return operationStatsMap.keySet(); } @Override public OperationStats getOperationStats(String operation) { return operationStatsMap.get(operation); } @Override public <T> T getRepresentation(String operationName, Class<T> clazz, Object... args) { OperationStats operationStats = operationStatsMap.get(operationName); if (operationStats == null) { // it's always going to be the first element in Basic Statistics operationStats = getOperationStatsForGroups().get(0).get(operationName); } if (operationStats == null) { return prototype.getRepresentation(clazz, this, args); } return operationStats.getRepresentation(clazz, this, args); } @Override public String toString() { return super.toString() + "{" + operationStatsMap + "}"; } }