package org.radargun.config; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import org.radargun.Stage; import org.radargun.reporting.Report; import org.radargun.state.StateBase; import org.radargun.utils.MapEntry; import org.radargun.utils.Utils; /** * List of stages in order in which these should be executed. * * @author Radim Vansa <rvansa@redhat.com> */ public class Scenario implements Serializable { private List<StageDescription> stages = new ArrayList<>(); private Map<String, Integer> labels = new HashMap<>(); public List<StageDescription> getStages() { return stages; } /** * @param stageClass * @param properties Stage's attributes as written in configuration - evaluation takes place on slave. * @param labelName Label that should be used together with stage-defined prefix and suffix to uniquely * identify this stage in the scenario. Can be null. */ public void addStage(Class<? extends Stage> stageClass, Map<String, Definition> properties, String labelName) { org.radargun.config.Stage annotation = stageClass.getAnnotation(org.radargun.config.Stage.class); if (annotation == null) { throw new IllegalArgumentException("Class " + stageClass.getName() + " does not have annotation @Stage!"); } Label label = annotation.label(); String fullLabelName = Utils.concat(label.separator(), label.prefix(), labelName, label.suffix()); if (!fullLabelName.isEmpty()) { if (labels.put(fullLabelName, stages.size()) != null) { throw new IllegalArgumentException("Label '" + label + "' was already defined (hint: two loops without name?)"); } } stages.add(new StageDescription(stageClass, properties)); } /** * Get instance of stage with given ID, using additional properties (evaluable as ${foo}) from localExtras. * * @param stageId ID of the executed stage. * @param state Master's or slave's state - used to resolve the properties. * @param localExtras Additional properties that could be used for property value resolution. * @param report Report where the stage execution should be recorded. * @return Instance of the stage with properties set (not initialized and not injected with traits yet). */ public org.radargun.Stage getStage(int stageId, StateBase state, Map<String, String> localExtras, Report report) { StageDescription description = stages.get(stageId); org.radargun.Stage stage; try { stage = description.stageClass.newInstance(); } catch (Exception e) { throw new RuntimeException("Cannot instantiate " + description.stageClass.getName(), e); } PropertyHelper.setPropertiesFromDefinitions(stage, description.properties, localExtras, state != null ? state.asStringMap() : Collections.EMPTY_MAP); if (report != null) { Report.Stage reportStage = report.addStage(stage.getName()); recordStage(reportStage, stage, description); } return stage; } private void recordStage(Report.Stage reportStage, org.radargun.Stage stage, StageDescription description) { // expand complex definitions Queue<Map.Entry<String, Definition>> queue = new LinkedList<>(description.properties.entrySet()); HashMap<String, Definition> expanded = new HashMap<>(); Map.Entry<String, Definition> entry; while ((entry = queue.poll()) != null) { if (entry.getValue() instanceof SimpleDefinition) { expanded.put(entry.getKey(), entry.getValue()); } else if (entry.getValue() instanceof ComplexDefinition) { for (ComplexDefinition.Entry subentry : ((ComplexDefinition) entry.getValue()).getAttributes()) { queue.add(new MapEntry<>(entry.getKey() + "-" + subentry.name, subentry.definition)); } expanded.put(entry.getKey(), entry.getValue()); } else throw new IllegalArgumentException("Unknown definition type: " + entry.getValue()); } for (Map.Entry<String, Path> property : PropertyHelper.getProperties(description.stageClass, true, false, false).entrySet()) { String propertyName = property.getKey(); Path path = property.getValue(); Definition definition = expanded.get(propertyName); reportStage.addProperty(propertyName, definition, PropertyHelper.getPropertyString(path, stage)); } } /** * @return Total number of stages in the scenario. All stage IDs should be < this number. */ public int getStageCount() { return stages.size(); } /** * @param label Full label of the stage. * @return Stage ID of stage with given label. */ public int getLabel(String label) { Integer id = labels.get(label); return id == null ? -1 : id; } public static class StageDescription implements Serializable { Class<? extends org.radargun.Stage> stageClass; /* Common properties as specified in configuraion */ Map<String, Definition> properties; private StageDescription(Class<? extends org.radargun.Stage> stageClass, Map<String, Definition> properties) { this.stageClass = stageClass; this.properties = properties; } } }