diff --git a/CUSTOM_EXTENSIONS.md b/CUSTOM_EXTENSIONS.md index 6367b3b..adb7729 100644 --- a/CUSTOM_EXTENSIONS.md +++ b/CUSTOM_EXTENSIONS.md @@ -1,4 +1,4 @@ -

Correlations Recorder Plugin for JMeter

+

Auto Correlation Recorder Plugin for JMeter

Custom implementation of Correlations

# Summary @@ -181,4 +181,4 @@ Review these links for a further understanding of correlating concepts and examp * [Siebel's Custom Extension explained](customizing/siebel_extension_explanations.md): an explanation of Siebel CRM’s Custom Extension. * [Extensions and useful methods in the Flow](customizing/the_flow_explanation.md): detailed explanation of how correlation works. -* [Examples](examples): basic structure for a Correlation Extension. \ No newline at end of file +* [Examples](examples): basic structure for a Correlation Extension. diff --git a/README.md b/README.md index fa98d14..21219f4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Correlations Recorder Plugin for JMeter +# Auto Correlations Recorder Plugin for JMeter ![BlazeMeter Labs](docs/src/.vuepress/public/images/blazemeter-labs-logo.png) -Welcome to the Correlation Recorder's JMeter Plugin, the main and extensive documentation can be seen in [this link](https://blazemeter.github.io/CorrelationRecorder/). +Welcome to the Auto Correlation Recorder's JMeter Plugin, the main and extensive documentation can be seen in [this link](https://blazemeter.github.io/CorrelationRecorder/). ## Usage diff --git a/pom.xml b/pom.xml index 146438b..5b179a6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,9 +6,9 @@ com.blazemeter jmeter-bzm-correlation-recorder jar - 2.5 - Correlation Recorder as JMeter plugin - Correlation Recorder Plugin for JMeter + 2.5.1 + Auto Correlation Recorder + Auto Correlation Recorder as JMeter Plugin https://github.com/Blazemeter/CorrelationRecorder diff --git a/releases.json b/releases.json new file mode 100644 index 0000000..a5b7995 --- /dev/null +++ b/releases.json @@ -0,0 +1,16 @@ +[ + { + "version": "2.5.1", + "what_is_new": "Draft template feature and performance improvements", + "dependencies": [ + "jackson-dataformat-xml>=2.10.3", + "jackson-annotations>=2.13.3", + "jackson-databind>=2.10.3", + "jackson-core>=2.13.3", + "jackson-module-jaxb-annotations>=2.10.3", + "jmeter-bzm-commons>=0.2.1", + "json>=20190722", + "maven-artifact>=3.8.4" + ] + } +] diff --git a/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControl.java b/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControl.java index 9674f6f..8b4a21a 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControl.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControl.java @@ -92,7 +92,7 @@ public class CorrelationProxyControl extends ProxyControl implements private static final String TEMPLATE_PATH = "CorrelationProxyControl.templatePath"; private static final String CORRELATION_HISTORY_ID = "CorrelationProxyControl.correlationHistoryId"; - private static final String RECORDER_NAME = "bzm - Correlation Recorder"; + private static final String RECORDER_NAME = "bzm - Auto Correlation Recorder"; // we use reflection to be able to call these non visible methods and not have to re implement // them. private static final Method FIND_FIRST_NODE_OF_TYPE = getProxyControlMethod("findFirstNodeOfType", @@ -291,7 +291,7 @@ public CorrelationRulesTestElement getCorrelationRulesTestElement() { @Override public synchronized void deliverSampler(HTTPSamplerBase sampler, TestElement[] testElements, - SampleResult result) { + SampleResult result) { if (pendingProxies.containsKey(Thread.currentThread())) { pendingProxies.get(Thread.currentThread()).update(sampler, testElements, result); } else { @@ -384,7 +384,7 @@ private void deliverCompletedProxy(PendingProxy proxy) { HTTPSamplerBase sampler = proxy.getSampler(); if (sampler != null) { sampler.setProperty("TestPlan.comments", "ORIGINAL_NAME USED BY CR, DON'T DELETE." + - " Prepend addition comments if necessary.;ORIGINAL_NAME=" + sampler.getName()); + " Prepend addition comments if necessary.;ORIGINAL_NAME=" + sampler.getName()); } // Ideal solution would be an invisible field or something like the following, // but it is not being saved to testplan @@ -526,13 +526,16 @@ public JMeterTreeNode findTargetControllerNode() { @Override public void onSaveTemplate(Builder builder) throws IOException, ConfigurationException { - Template template = builder + Template template = getTemplate(builder); + localConfiguration.saveTemplate(template); + } + + public Template getTemplate(Builder builder) { + return builder .withGroups(getGroups()) .withComponents(getCorrelationComponents()) .withResponseFilters(getResponseFilter()) .build(); - - localConfiguration.saveTemplate(template); } public List getGroups() { @@ -614,7 +617,7 @@ public void onLoadTemplate(String repositoryOwner, String id, String templateVer } private void append(String loadedComponents, List loadedGroups, - String loadedFilters) { + String loadedFilters) { String actualComponents = getCorrelationComponents(); setCorrelationComponents(loadedComponents.isEmpty() ? actualComponents @@ -775,8 +778,8 @@ public List checkURL(String id, String url) { @Override public boolean refreshRepositories(String localConfigurationRoute, - Consumer setProgressConsumer, - Consumer setStatusConsumer) { + Consumer setProgressConsumer, + Consumer setStatusConsumer) { return localConfiguration.refreshRepositories(localConfigurationRoute, setProgressConsumer, setStatusConsumer); } @@ -806,8 +809,8 @@ private List getCorrelationRulesFromTestElement( } private void updateExtractorFromTestElement(CorrelationRuleTestElement e, - CorrelationRule correlationRule, - String referenceName) { + CorrelationRule correlationRule, + String referenceName) { try { //Only when no Extractor was selected, this method returns null correlationRule.setCorrelationExtractor(e.getCorrelationExtractor()); @@ -818,8 +821,8 @@ private void updateExtractorFromTestElement(CorrelationRuleTestElement e, } private void updateReplacementFromTestElement(CorrelationRuleTestElement e, - CorrelationRule correlationRule, - String referenceName) { + CorrelationRule correlationRule, + String referenceName) { try { //Only when no Replacement was selected, this method returns null correlationRule.setCorrelationReplacement(e.getCorrelationReplacement()); diff --git a/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlGui.java b/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlGui.java index 571a422..3568529 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlGui.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlGui.java @@ -66,6 +66,7 @@ public CorrelationProxyControlGui() { wizard = new CorrelationWizard(); wizard.setRepositoriesSupplier(this::getRepositories); wizard.setAddRuleConsumer(rulesContainer.obtainRulesExporter()); + wizard.setBuildTemplateProvider((builderTemplate) -> model.getTemplate(builderTemplate)); wizard.init(); rulesContainer.setOnWizardDisplayMethod(() -> wizard.displayMethodSelection()); @@ -138,7 +139,7 @@ private JTabbedPane findTabbedPane() { @Override public String getStaticLabel() { - return "bzm - Correlation Recorder"; + return "bzm - Auto Correlation Recorder"; } @Override diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/CorrelationSuggestion.java b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/CorrelationSuggestion.java index b752585..f703fe4 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/CorrelationSuggestion.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/CorrelationSuggestion.java @@ -23,6 +23,7 @@ public class CorrelationSuggestion { private final List appearances = new ArrayList<>(); private final List usages = new ArrayList<>(); private final List extractionSuggestions = new ArrayList<>(); + private final List extractionSuggestionsString = new ArrayList<>(); private final List replacementSuggestions = new ArrayList<>(); private String method = "Replay"; @@ -91,6 +92,7 @@ public List getUsages() { public void addExtractionSuggestion(ExtractionSuggestion extractionSuggestion) { extractionSuggestions.add(extractionSuggestion); + extractionSuggestionsString.add(extractionSuggestion.toString()); } public void addReplacementSuggestion(ReplacementSuggestion replacementSuggestion) { @@ -101,6 +103,10 @@ public List getExtractionSuggestions() { return extractionSuggestions; } + public List getExtractionSuggestionsString() { + return extractionSuggestionsString; + } + public List getReplacementSuggestions() { return replacementSuggestions; } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ExtractorGenerator.java b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ExtractorGenerator.java index ac50dd8..f8aec54 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ExtractorGenerator.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ExtractorGenerator.java @@ -10,8 +10,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -152,15 +151,26 @@ public List getIndexes() { return indexes; } - public static List getIndexes(String v, String text) { - if (v.isEmpty()) { + public static List getIndexes(String search, String text) { + if (text.isEmpty()) { return new ArrayList<>(); } - - return IntStream.range(0, text.length() - v.length() + 1) - .filter(index -> text.substring(index, index + v.length()).equalsIgnoreCase(v)) - .boxed() - .collect(Collectors.toList()); + List list = new ArrayList<>(); + final int len = search.length(); + final int max = text.length() - len; + int foundIndex = -1; + int i = 0; + while (i <= max) { + foundIndex = StringUtils.indexOf(text, search, i); + if (foundIndex != -1) { + list.add(foundIndex); + i = foundIndex; + } else { + break; // When not found, break the search by position + } + i++; + } + return list; } public void generateExtractors() { diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/JMeterElementUtils.java b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/JMeterElementUtils.java index bf4e992..173846e 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/JMeterElementUtils.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/JMeterElementUtils.java @@ -83,6 +83,7 @@ public class JMeterElementUtils { protected static final String URL_PARAM_SEPARATOR = "&"; protected static final String URL_PARAM_VALUE_SEPARATOR = "="; private static final Logger LOG = LoggerFactory.getLogger(JMeterElementUtils.class); + private Configuration configuration; public JMeterElementUtils() { @@ -245,16 +246,18 @@ private boolean isIgnoredHeader(String key) { } public static boolean isJson(String value) { + boolean startJson = StringUtils.startsWithAny( + StringUtils.trim(StringUtils.truncate(value, 100)), "{", "["); + if (value.isEmpty() || !startJson) { + return false; + } + boolean isJson = true; try { - new JSONObject(value); - } catch (JSONException ex) { - try { - new JSONArray(value); - } catch (JSONException exception) { - return false; - } + JsonPath.parse(value); + } catch (Exception ex) { + isJson = false; } - return true; + return isJson; } public static boolean isXml(String xml) { diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/JsonUtils.java b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/JsonUtils.java index f301ace..3573731 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/JsonUtils.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/JsonUtils.java @@ -1,10 +1,9 @@ package com.blazemeter.jmeter.correlation.core.automatic; import com.fasterxml.jackson.databind.JsonNode; -import java.util.Arrays; import java.util.Iterator; -import java.util.List; import java.util.Map.Entry; +import org.apache.commons.lang3.StringUtils; public class JsonUtils { @@ -12,20 +11,24 @@ public static String findPath(JsonNode node, String valueToFind) { if (node.asText().equals(valueToFind)) { return ""; } + String key; + Entry entry; + JsonNode value; + String currentPath; if (node.isObject()) { Iterator> it = node.fields(); while (it.hasNext()) { - Entry entry = it.next(); - String key = handleSpecialCharactersInJsonKey(entry.getKey()); - JsonNode value = entry.getValue(); - String currentPath = findPath(value, valueToFind); + entry = it.next(); + key = handleSpecialCharactersInJsonKey(entry.getKey()); + value = entry.getValue(); + currentPath = findPath(value, valueToFind); if (currentPath != null) { return "." + key + (currentPath); } } } else if (node.isArray()) { for (int i = 0; i < node.size(); i++) { - String currentPath = findPath(node.get(i), valueToFind); + currentPath = findPath(node.get(i), valueToFind); if (currentPath != null) { return (currentPath.startsWith(".") ? "." : "..") + currentPath; } @@ -35,9 +38,7 @@ public static String findPath(JsonNode node, String valueToFind) { } private static String handleSpecialCharactersInJsonKey(String key) { - List specialCharacters = Arrays.asList(".", "$", "@", "[", "]", "?", "(", ")"); - boolean containJsonPathExpression = specialCharacters.stream().anyMatch(key::contains); - if (containJsonPathExpression) { + if (StringUtils.containsAny(key, ".$@[]?()")) { return "['" + key + "']"; } return key; diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ResponseAnalyzer.java b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ResponseAnalyzer.java index 9d1f558..90e066a 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ResponseAnalyzer.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ResponseAnalyzer.java @@ -7,6 +7,7 @@ import com.blazemeter.jmeter.correlation.core.automatic.extraction.location.HeaderExtractionStrategy; import com.blazemeter.jmeter.correlation.core.automatic.extraction.location.LocationType; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import org.apache.jmeter.samplers.SampleResult; @@ -17,16 +18,20 @@ public class ResponseAnalyzer { private final List strategies = Arrays.asList( - new BodyExtractionStrategy(), new HeaderExtractionStrategy(), - new CookieExtractionStrategy() + new CookieExtractionStrategy(), + new BodyExtractionStrategy() ); + private HashMap resposeStructureTypeCache = + new HashMap(); + /** * Identifies the location of an argument in a response. * Uses a list of {@link ExtractionStrategy} to identify the location. + * * @param response The response to analyze - * @param value The value to search in the response + * @param value The value to search in the response * @return The location of the argument in the response * @see ExtractionStrategy */ @@ -46,25 +51,30 @@ public LocationType identifyArgumentLocation(SampleResult response, String value * StructureType#XML}, {@link StructureType#RAW_TEXT} or {@link StructureType#UNKNOWN}. * This is particularly useful when the argument is in the body of the response and we need to * know the structure of the body to generate the extractor. - * @param response The response to analyze + * + * @param response The response to analyze * @param locationType The location of the argument in the response * @return The structure of the argument in the response * @see StructureType */ public StructureType identifyStructureType(SampleResult response, LocationType locationType) { - if (locationType == LocationType.HEADER || locationType == LocationType.COOKIE) { - return StructureType.RAW_TEXT; - } else if (locationType == LocationType.BODY) { - if (JMeterElementUtils.isJson(response.getResponseDataAsString())) { - return StructureType.JSON; - } - - if (JMeterElementUtils.isXml(response.getResponseDataAsString())) { - return StructureType.XML; + StructureType structureType = StructureType.UNKNOWN; + String responseKey = response.getSampleLabel() + ":" + locationType; + if (resposeStructureTypeCache.containsKey(responseKey)) { + return resposeStructureTypeCache.get(responseKey); + } else { + if (locationType == LocationType.HEADER || locationType == LocationType.COOKIE) { + return StructureType.RAW_TEXT; + } else if (locationType == LocationType.BODY) { + if (JMeterElementUtils.isJson(response.getResponseDataAsString())) { + structureType = StructureType.JSON; + } else { + structureType = StructureType.RAW_TEXT; + } } - - return StructureType.RAW_TEXT; } - return StructureType.UNKNOWN; + resposeStructureTypeCache.put(responseKey, structureType); + return structureType; } + } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ResultsExtraction.java b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ResultsExtraction.java index cae13e9..d8c3137 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ResultsExtraction.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/ResultsExtraction.java @@ -62,7 +62,7 @@ public ResultsExtraction(Configuration configuration) { * Convert string to cookie. * * @param cookieStr the cookie as a string - * @param url to extract domain and path for the cookie from + * @param url to extract domain and path for the cookie from * @return list of cookies */ public static List stringToCookie(String cookieStr, String url) { @@ -289,14 +289,17 @@ private void extractAppearancesFromResults(List results) { RecordingExtraction samplersExtractor = new RecordingExtraction(configuration, appearanceMap); for (SampleResult result : results) { if (result instanceof HTTPSampleResult) { - - HTTPSampleResult httpSampleResult = (HTTPSampleResult) result; - HTTPSamplerProxy sourceRequest = parseToHttpSampler(httpSampleResult); - if (requestsMap != null && requestsMap.containsKey(result.getSampleLabel())) { - sourceRequest = requestsMap.get(result.getSampleLabel()); + try { + HTTPSampleResult httpSampleResult = (HTTPSampleResult) result; + HTTPSamplerProxy sourceRequest = parseToHttpSampler(httpSampleResult); + if (requestsMap != null && requestsMap.containsKey(result.getSampleLabel())) { + sourceRequest = requestsMap.get(result.getSampleLabel()); + } + samplersExtractor.extractParametersFromHttpSampler(sourceRequest); + extractAppearancesFromSampleResult(httpSampleResult, sourceRequest); + } catch (Exception ex) { //Capture any exception to avoid teardown all flow + LOG.error("Error Extracting Parameters from result", ex); } - samplersExtractor.extractParametersFromHttpSampler(sourceRequest); - extractAppearancesFromSampleResult(httpSampleResult, sourceRequest); } } } @@ -314,13 +317,17 @@ private void extractAppearancesFromSampleResult(HTTPSampleResult httpSampleResul } private void extractAppearancesFromJson(String json, HTTPSamplerProxy sourceRequest) { - if (JMeterElementUtils.isJsonArray(json)) { - utils.extractParametersFromJsonArray(new JSONArray(json), appearanceMap, sourceRequest, "", + try { + if (JMeterElementUtils.isJsonArray(json)) { + utils.extractParametersFromJsonArray(new JSONArray(json), appearanceMap, sourceRequest, "", + Sources.RESPONSE_BODY_JSON); + return; + } + utils.extractParametersFromJson(new JSONObject(json), appearanceMap, sourceRequest, Sources.RESPONSE_BODY_JSON); - return; + } catch (Exception ex) { + LOG.error("Error parsing JSON", ex); } - utils.extractParametersFromJson(new JSONObject(json), appearanceMap, sourceRequest, - Sources.RESPONSE_BODY_JSON); } private void extractParametersFromHeaderStrings(String headerString, diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/Sources.java b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/Sources.java index a3e875e..b505441 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/Sources.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/Sources.java @@ -1,6 +1,7 @@ package com.blazemeter.jmeter.correlation.core.automatic; public class Sources { + public static final String REQUEST_BODY_JSON = "Request Body JSON"; public static final String REQUEST_BODY_JSON_NUMERIC = "Request Body JSON (Numeric)"; public static final String RESPONSE_BODY_JSON_NUMERIC = "Response Body JSON (Numeric)"; @@ -18,4 +19,15 @@ public class Sources { public static final String RESPONSE_BODY_JSON = "Response Body JSON"; public static final String REQUEST = "Header"; + public static boolean isRequestSource(String source) { + return REQUEST_BODY_JSON.equals(source) + || REQUEST_BODY_JSON_NUMERIC.equals(source) + || REQUEST_HEADER_FIELDS.equals(source) + || REQUEST_PATH.equals(source) + || REQUEST_PATH_NUMBER_FOLLOWED_BY_QUESTION_MARK.equals(source) + || REQUEST_PATH_NUMBER_FOLLOWED_BY_SLASH.equals(source) + || REQUEST_ARGUMENTS.equals(source) + || REQUEST_URL.equals(source) + || REQUEST_QUERY.equals(source); + } } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/extraction/method/JsonBodyExtractor.java b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/extraction/method/JsonBodyExtractor.java index 3aabeda..59c6cc3 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/extraction/method/JsonBodyExtractor.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/extraction/method/JsonBodyExtractor.java @@ -9,9 +9,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.jmeter.processor.PostProcessor; import org.apache.jmeter.samplers.SampleResult; @@ -23,6 +25,8 @@ public class JsonBodyExtractor extends Extractor { private static final Logger LOG = LoggerFactory.getLogger(JsonBodyExtractor.class); private final ObjectMapper objectMapper; + private HashMap ssonNodeCache = new HashMap(); + public JsonBodyExtractor() { this.objectMapper = new ObjectMapper(); } @@ -52,16 +56,21 @@ public List getPostProcessors(SampleResult response, String value @Override public List> getCorrelationExtractors(SampleResult response, String value, - String name) { - String path; + String name) { + String path = null; try { - JsonNode root = objectMapper.readTree(response.getResponseDataAsString()); - path = JsonUtils.findPath(root, value); + JsonNode root; + if (ssonNodeCache.containsKey(response.getSampleLabel())) { + root = ssonNodeCache.get(response.getSampleLabel()); + } else { + root = objectMapper.readTree(response.getResponseDataAsString()); + ssonNodeCache.put(response.getSampleLabel(), root); + } + int foundIndex = StringUtils.indexOf(response.getResponseDataAsString(), value); + if (foundIndex != -1) { + path = JsonUtils.findPath(root, value); + } if (path == null) { - if (LOG.isDebugEnabled()) { - System.out.printf( - "No match found for '%s' in response '%s'", value, response.getSampleLabel()); - } return Collections.emptyList(); } } catch (JsonProcessingException e) { diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/extraction/method/RawTextBodyExtractor.java b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/extraction/method/RawTextBodyExtractor.java index f54b1b5..847e778 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/extraction/method/RawTextBodyExtractor.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/automatic/extraction/method/RawTextBodyExtractor.java @@ -27,8 +27,9 @@ public List> getCorrelationExtractors(SampleResult respo String responseDataAsString = response.getResponseDataAsString(); List indexes = ExtractorGenerator.getIndexes(value, responseDataAsString); List> extractors = new ArrayList<>(); + String context; for (int index : indexes) { - String context = getContextString(responseDataAsString, value, index); + context = getContextString(responseDataAsString, value, index); RegexCorrelationExtractor extractor = generateExtractor(name, value, context, ResultField.BODY); extractor.setMultiValued(true); diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/replacements/CorrelationReplacement.java b/src/main/java/com/blazemeter/jmeter/correlation/core/replacements/CorrelationReplacement.java index c0dce39..75b1fe9 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/replacements/CorrelationReplacement.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/replacements/CorrelationReplacement.java @@ -37,8 +37,8 @@ * * Contains all the methods from loading, saving and processing requests from the server. * - * For a more detailed explanation on Correlation Replacements, their usages and methods, please - * read the + * For a more detailed explanation on Correlation Replacements, their usages and methods, please + * read the * readme. * * Along side {@link com.blazemeter.jmeter.correlation.core.extractors.CorrelationExtractor} @@ -96,8 +96,8 @@ public void setVariableName(String variableName) { } /** - * Handles the saving of values of the Correlation Replacement into a {@link - * CorrelationRuleTestElement} for later storage in Test Plans and CorrelationTemplates. + * Handles the saving of values of the Correlation Replacement into a + * {@link CorrelationRuleTestElement} for later storage in Test Plans and CorrelationTemplates. * *

This method has to be overwritten when implementing custom Correlation Replacements, * otherwise, only the class name will be saved @@ -115,14 +115,14 @@ public void updateTestElem(CorrelationRuleTestElement testElem) { * *

Both the properties in the recorded sampler and its children will be processed * - * @param sampler recorded sampler containing the information of the request + * @param sampler recorded sampler containing the information of the request * @param children list of children added to the sampler (if the condition is matched, components - * will be added to it to correlate the obtained values) - * @param result result containing information about request and associated response from server - * @param vars stored variables shared between requests during recording + * will be added to it to correlate the obtained values) + * @param result result containing information about request and associated response from server + * @param vars stored variables shared between requests during recording */ public void process(HTTPSamplerBase sampler, List children, SampleResult result, - JMeterVariables vars) { + JMeterVariables vars) { replaceTestElementProperties(sampler, vars); for (TestElement child : children) { if (child instanceof ConfigTestElement) { @@ -140,7 +140,7 @@ public void process(HTTPSamplerBase sampler, List children, SampleR * Replacement, the value will be replaced in the String as ${referenceVariableName}, * as many times as the logic in the condition allows it. * - * @param el test element to check and match the properties + * @param el test element to check and match the properties * @param vars stored variables from the recording */ private void replaceTestElementProperties(TestElement el, JMeterVariables vars) { @@ -165,12 +165,20 @@ private JMeterProperty replaceProperty(JMeterProperty prop, JMeterVariables vars if (!prop.getName().equals(TestElement.GUI_CLASS) && !prop.getName().equals(TestElement.TEST_CLASS)) { prop = replaceSimpleProp(prop, vars); - LOG.debug("CorrelationReplacement result: {}", prop); } } else if (prop instanceof NumberProperty) { prop = replaceSimpleProp(prop, vars); - LOG.debug("CorrelationReplacement result: {}", prop); } else if (prop instanceof MultiProperty) { + if (prop instanceof TestElementProperty) { + TestElementProperty testElementProperty = (TestElementProperty) prop; + if (testElementProperty.getObjectValue() instanceof Argument) { + replaceArgument((Argument) testElementProperty.getObjectValue(), vars); + return prop; + } else if (testElementProperty.getObjectValue() instanceof Header) { + replaceHeader((Header) testElementProperty.getObjectValue(), vars); + return prop; + } + } MultiProperty multiVal = (MultiProperty) prop; PropertyIterator propertyIterator = multiVal.iterator(); Collection newValues = new ArrayList<>(); @@ -182,19 +190,10 @@ private JMeterProperty replaceProperty(JMeterProperty prop, JMeterVariables vars for (JMeterProperty jmp : newValues) { multiVal.addProperty(jmp); } - if (multiVal instanceof TestElementProperty) { - TestElementProperty multiProp = (TestElementProperty) multiVal; - if (multiProp.getObjectValue() instanceof Argument) { - replaceArgument((Argument) multiProp.getObjectValue(), vars); - } else if (multiProp.getObjectValue() instanceof Header) { - replaceHeader((Header) multiProp.getObjectValue(), vars); - } - } - LOG.debug("CorrelationReplacement result: {}", multiVal); - } else { LOG.debug("Won't replace {}", prop); } + LOG.debug("CorrelationReplacement result: {}", prop); return prop; } @@ -212,7 +211,7 @@ private JMeterProperty replaceSimpleProp(JMeterProperty prop, JMeterVariables va * implemented. * * @param input property's string to check and replace - * @param vars stored variables shared between request during the recording + * @param vars stored variables shared between request during the recording * @return the resultant input after been processed */ protected abstract String replaceString(String input, JMeterVariables vars); @@ -224,12 +223,17 @@ private void replaceArgument(Argument arg, JMeterVariables vars) { } /* To normalize the replacement on arguments for HTTP requests, we include the argument name and - '=' to the input, apply the replacement logic, and remove it afterward. This doesn't applies + '=' to the input, apply the replacement logic, and remove it afterward. This doesn't applies when the argument has no name (eg: Data Body is a JSON/XML). */ String prefix = arg.getName().isEmpty() ? "" : arg.getName() + "="; input = replaceString(prefix + arg.getValue(), vars).replace(prefix, ""); arg.setValue(input); + /* + In order to comply backward compatibility from <=v2.5 keys (arg name) is also processed by + the replacement + */ + arg.setName(replaceString(arg.getName(), vars)); } private void replaceHeader(Header header, JMeterVariables vars) { @@ -240,6 +244,11 @@ private void replaceHeader(Header header, JMeterVariables vars) { input = replaceString(header.getName() + ": " + header.getValue(), vars) .replace(header.getName() + ": ", ""); header.setValue(input); + /* + In order to comply backward compatibility from <=v2.5 keys (header name) is also processed by + the replacement + */ + header.setName(replaceString(header.getName(), vars)); } @Override diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/suggestions/method/ComparisonMethod.java b/src/main/java/com/blazemeter/jmeter/correlation/core/suggestions/method/ComparisonMethod.java index ca1fb54..7700f7b 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/suggestions/method/ComparisonMethod.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/suggestions/method/ComparisonMethod.java @@ -10,6 +10,7 @@ import com.blazemeter.jmeter.correlation.core.automatic.Sources; import com.blazemeter.jmeter.correlation.core.automatic.extraction.StructureType; import com.blazemeter.jmeter.correlation.core.automatic.extraction.location.LocationType; +import com.blazemeter.jmeter.correlation.core.automatic.extraction.method.Extractor; import com.blazemeter.jmeter.correlation.core.automatic.extraction.method.ExtractorFactory; import com.blazemeter.jmeter.correlation.core.automatic.replacement.method.ReplacementContext; import com.blazemeter.jmeter.correlation.core.extractors.CorrelationExtractor; @@ -17,7 +18,6 @@ import com.blazemeter.jmeter.correlation.core.suggestions.context.ComparisonContext; import com.blazemeter.jmeter.correlation.core.suggestions.context.CorrelationContext; import com.helger.commons.annotation.VisibleForTesting; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -46,6 +46,8 @@ public class ComparisonMethod implements CorrelationMethod { private final List orphanSuggestions = new ArrayList<>(); private List results; private ComparisonContext context; + private final HashMap structureTypeCache = + new HashMap<>(); public ComparisonMethod() { } @@ -83,10 +85,10 @@ private static boolean hasOrphans(CorrelationSuggestion suggestion) { * @return the populated ExtractionSuggestion. */ private static ExtractionSuggestion generateCandidateExtractor(SampleResult result, - Appearances appearance, - CorrelationExtractor extractor, - StructureType structureType, - String name) { + Appearances appearance, + CorrelationExtractor extractor, + StructureType structureType, + String name) { ExtractionSuggestion suggestion = new ExtractionSuggestion(extractor, result); suggestion.setSource(structureType.name()); suggestion.setValue(appearance.getValue()); @@ -114,7 +116,6 @@ public List generateSuggestions(CorrelationContext contex context.getClass()); return new ArrayList<>(); } - this.context = (ComparisonContext) context; this.results = this.context.getRecordingSampleResults(); // Load the results once @@ -166,7 +167,7 @@ private void loadFromDynamicElements(DynamicElement replayCandidate) { * @return the populated CorrelationSuggestion. */ private CorrelationSuggestion populateSuggestion(DynamicElement element, - CorrelationSuggestion suggestion) { + CorrelationSuggestion suggestion) { addMultivaluedExtractor(element, suggestion, results, valueToReferenceName); addMultivaluedReplacement(element, suggestion, valueToReferenceName); return suggestion; @@ -187,16 +188,15 @@ private CorrelationSuggestion populateSuggestion(DynamicElement element, * extraction suggestions. */ private void addMultivaluedExtractor(DynamicElement element, - CorrelationSuggestion suggestion, List results, - Map valueToReferenceName) { + CorrelationSuggestion suggestion, List results, + Map valueToReferenceName) { for (SampleResult result : results) { //We use both the "original" and the "other" appearances since the map can come from either //the original recorder or from the failing replay - addExtractorSuggestions(valueToReferenceName, suggestion, result, - element.getOriginalAppearance()); - addExtractorSuggestions(valueToReferenceName, suggestion, result, - element.getOtherAppearance()); + List appearancesList = new ArrayList<>(element.getOriginalAppearance()); + appearancesList.addAll(element.getOtherAppearance()); + addExtractorSuggestions(valueToReferenceName, suggestion, result, appearancesList); } } @@ -220,11 +220,22 @@ private void addMultivaluedExtractor(DynamicElement element, * suggestions. */ private void addExtractorSuggestions(Map valueToReferenceName, - CorrelationSuggestion suggestion, SampleResult result, - List appearances) { + CorrelationSuggestion suggestion, SampleResult result, + List appearances) { + structureTypeCache.clear(); + // Flowing fields declared beforehand for performance proposes + StructureType structureType; + ExtractorFactory ef = new ExtractorFactory(context.getConfiguration()); + HashMap extractorCache = new HashMap<>(); + ResponseAnalyzer analyzer = new ResponseAnalyzer(); + String name; + Extractor extractor; for (Appearances appearance : appearances) { - String name = suggestion.getParamName(); + if (!Sources.isRequestSource(appearance.getSource())) { + continue; + } + name = suggestion.getParamName(); if (suggestion.getAppearances().size() > getConfiguration().getMaxNumberOfAppearances() && getConfiguration().getMaxNumberOfAppearances() != -1) { LOG.warn("Too many appearances for element '{}'. Please review the total appearances.", @@ -238,18 +249,15 @@ && getConfiguration().getMaxNumberOfAppearances() != -1) { continue; } - ResponseAnalyzer analyzer = new ResponseAnalyzer(); LocationType locationType = analyzer.identifyArgumentLocation(result, appearance.getValue()); if (locationType == LocationType.UNKNOWN) { - LOG.debug("Couldn't associate a location for the param '{}' in the responses of {}. " - + "Value {}. Skipping this value.", name, result.getSampleLabel(), - appearance.getValue()); + // "Couldn't associate a location for the param in the responses. + // Skipping this value. continue; } - - StructureType structureType = analyzer.identifyStructureType(result, locationType); - List> extractors = new ExtractorFactory(context.getConfiguration()) - .getExtractor(locationType, structureType) + structureType = getStructureType(result, structureTypeCache, locationType, analyzer); + extractor = getExtractor(locationType, structureType, extractorCache, ef); + List> extractors = extractor .getCorrelationExtractors(result, appearance.getValue(), name); if (extractors == null || extractors.isEmpty()) { @@ -272,6 +280,33 @@ && getConfiguration().getMaxNumberOfAppearances() != -1) { } } + private static Extractor getExtractor(LocationType locationType, StructureType structureType, + HashMap extractorCache, ExtractorFactory ef) { + String extractorKey; + Extractor extractor; + extractorKey = locationType + ":" + structureType; + if (extractorCache.containsKey(extractorKey)) { + extractor = extractorCache.get(extractorKey); + } else { + extractor = ef.getExtractor(locationType, structureType); + extractorCache.put(extractorKey, extractor); + } + return extractor; + } + + private static StructureType getStructureType(SampleResult result, + HashMap structureTypeCache, LocationType locationType, + ResponseAnalyzer analyzer) { + StructureType structureType; + if (structureTypeCache.containsKey(locationType)) { + structureType = structureTypeCache.get(locationType); + } else { + structureType = analyzer.identifyStructureType(result, locationType); + structureTypeCache.put(locationType, structureType); + } + return structureType; + } + /** * This method converts a list of Appearances into a string representation. It first creates a map * of appearance values to their counts. Then, it converts the map into a string where each entry @@ -308,9 +343,7 @@ private String appearancesToString(List appearances) { */ private boolean valueInUse(SampleResult result, String value) { String queryString = this.resultQueryString.apply(result); - boolean encodedUsed = queryString.contains(URLEncoder.encode(value)); - boolean rawUsed = queryString.contains(value); - return encodedUsed || rawUsed; + return queryString.contains(value); } /** @@ -343,17 +376,8 @@ private boolean reachedUsageSampler(SampleResult result, List appea * @return true if the replacement suggestion is already present in the list, false otherwise. */ private boolean isRepeated(CorrelationSuggestion suggestion, - CorrelationReplacement replacementSuggestion) { - boolean repeated = false; - List replacementSuggestions = suggestion.getReplacementSuggestions(); - for (ReplacementSuggestion existing : replacementSuggestions) { - String s = existing.toString(); - if (s.equals(replacementSuggestion.toString())) { - repeated = true; - break; - } - } - return repeated; + CorrelationReplacement replacementSuggestion) { + return suggestion.getExtractionSuggestionsString().contains(replacementSuggestion.toString()); } /** @@ -369,15 +393,8 @@ private boolean isRepeated(CorrelationSuggestion suggestion, * @return true if the extraction suggestion is already present in the list, false otherwise. */ private boolean isRepeated(CorrelationSuggestion suggestion, - ExtractionSuggestion extractionSuggestion) { - boolean repeated = false; - for (ExtractionSuggestion existing : suggestion.getExtractionSuggestions()) { - if (existing.toString().equals(extractionSuggestion.toString())) { - repeated = true; - break; - } - } - return repeated; + ExtractionSuggestion extractionSuggestion) { + return suggestion.getExtractionSuggestionsString().contains(extractionSuggestion.toString()); } /** @@ -395,8 +412,8 @@ private boolean isRepeated(CorrelationSuggestion suggestion, * replacement suggestions. */ private void addMultivaluedReplacement(DynamicElement element, - CorrelationSuggestion suggestion, - Map valueToReferenceName) { + CorrelationSuggestion suggestion, + Map valueToReferenceName) { List originalAppearances = element.getOriginalAppearance(); List otherAppearances = element.getOtherAppearance(); @@ -422,8 +439,8 @@ private void addMultivaluedReplacement(DynamicElement element, * suggestions. */ private void addReplacementSuggestions(CorrelationSuggestion suggestion, - Map valueToReferenceName, - List originalAppearances) { + Map valueToReferenceName, + List originalAppearances) { String name = suggestion.getParamName(); for (Appearances appearance : originalAppearances) { String referenceName = valueToReferenceName.get(appearance.getValue()); diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfiguration.java b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfiguration.java index b9deb30..4932703 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfiguration.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfiguration.java @@ -66,10 +66,12 @@ public class LocalConfiguration { private static final String JAR_FILE_SUFFIX = ".jar"; private static final String TEMPLATES_FOLDER_PATH = "/templates/"; private static final String LOCAL_REPOSITORIES_MANAGERS_FILE_NAME = "managers"; - private static final String CORRELATION_RECORDER_TEST_PLAN = "correlation-recorder.jmx"; - private static final String CORRELATION_RECORDER_TEMPLATE_DESC = "correlation-recorder-template" + private static final String CORRELATION_RECORDER_TEST_PLAN = "auto-correlation-recorder.jmx"; + private static final String CORRELATION_RECORDER_TEMPLATE_DESC = "auto-correlation-recorder" + + "-template" + "-description.xml"; - private static final String CORRELATION_RECORDER_TEMPLATE_NAME = "bzm - Correlation Recorder"; + private static final String CORRELATION_RECORDER_TEMPLATE_NAME = "bzm - Auto Correlation " + + "Recorder"; private static final Logger LOG = LoggerFactory.getLogger(LocalConfiguration.class); @JsonIgnore diff --git a/src/main/java/com/blazemeter/jmeter/correlation/gui/TestPlanTemplatesRepository.java b/src/main/java/com/blazemeter/jmeter/correlation/gui/TestPlanTemplatesRepository.java index 0e5feb1..b8e2f4f 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/gui/TestPlanTemplatesRepository.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/gui/TestPlanTemplatesRepository.java @@ -14,6 +14,8 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -23,6 +25,7 @@ public class TestPlanTemplatesRepository { private static final Logger LOG = LoggerFactory.getLogger(TestPlanTemplatesRepository.class); + private static final String DEPRECATED_TEMPLATE_NAME = "correlation-recorder.jmx"; private String rootFolder; public TestPlanTemplatesRepository(String rootFolder) { @@ -45,7 +48,7 @@ public void setRootFolder(String rootFolder) { } public void addCorrelationRecorderTemplate(String templateFileName, String templatesFolderPath, - String descriptionFileName, String templateName) { + String descriptionFileName, String templateName) { copyTemplateFile(templateFileName, templatesFolderPath); addTemplateDescription(descriptionFileName, templateName); addFailExtractorAssertion(templateFileName); @@ -53,6 +56,7 @@ public void addCorrelationRecorderTemplate(String templateFileName, String templ private void copyTemplateFile(String fileName, String sourcePath) { try { + removeDeprecatedTemplate(DEPRECATED_TEMPLATE_NAME); File dest = new File(Paths.get(rootFolder, fileName).toAbsolutePath().toString()); String fileFromResources = getFileFromResources(sourcePath + fileName); if (!dest.exists() || !DigestUtils @@ -67,6 +71,21 @@ private void copyTemplateFile(String fileName, String sourcePath) { } } + private void removeDeprecatedTemplate( + @SuppressWarnings("SameParameterValue") String templateName) { + File oldTemplate = + new File(Paths.get(rootFolder, templateName).toAbsolutePath().toString()); + if (!oldTemplate.exists()) { + return; + } + LOG.info("[ACR] Removing old template: {}", oldTemplate.getAbsolutePath()); + if (!oldTemplate.delete()) { + LOG.error("[ACR] Failed to remove old template: {}", oldTemplate.getAbsolutePath()); + return; + } + LOG.info("[ACR] Successfully removed old template: {}", oldTemplate.getAbsolutePath()); + } + private String getFileFromResources(String fileName) throws IOException { InputStream inputStream = this.getClass().getResourceAsStream(fileName); return IOUtils.toString(inputStream, StandardCharsets.UTF_8); @@ -75,7 +94,7 @@ private String getFileFromResources(String fileName) throws IOException { private void addTemplateDescription(String descTemplateName, String templateName) { try { String filePath = Paths.get(rootFolder, "templates.xml").toAbsolutePath().toString(); - removeOldTemplate(filePath); + removeDeprecatedTemplatesDescription(filePath); if (!checkIfStringExists(filePath, "" + templateName + "")) { Path path = Paths.get(filePath); List replacedLines = new ArrayList<>(); @@ -96,7 +115,7 @@ private void addTemplateDescription(String descTemplateName, String templateName } } - private void removeOldTemplate(String filePath) { + private void removeDeprecatedTemplatesDescription(String filePath) { String content; try { @@ -105,19 +124,24 @@ private void removeOldTemplate(String filePath) { LOG.error("Error trying to read the file {}", filePath); return; } + List patterns = + Stream.of("Correlation Recorder", "bzm - Correlation Recorder") + .map(name -> Pattern.compile( + "()", + Pattern.DOTALL)) + .collect(Collectors.toList()); + for (Pattern pattern : patterns) { + Matcher matcher = pattern.matcher(content); + if (!matcher.find()) { + LOG.debug("Old Correlation Template not found."); + continue; + } - Matcher matcher = Pattern.compile( - "()", - Pattern.DOTALL).matcher(content); - if (!matcher.find()) { - LOG.debug("Old Correlation Template not found."); - return; - } - - try (FileWriter fileWriter = new FileWriter(filePath)) { - fileWriter.write(content.replace(matcher.group(), "")); - } catch (IOException e) { - LOG.error("Error trying to write the file {}", filePath); + try (FileWriter fileWriter = new FileWriter(filePath)) { + fileWriter.write(content.replace(matcher.group(), "")); + } catch (IOException e) { + LOG.error("Error trying to write the file {}", filePath); + } } } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/gui/analysis/CorrelationTemplatesSelectionPanel.java b/src/main/java/com/blazemeter/jmeter/correlation/gui/analysis/CorrelationTemplatesSelectionPanel.java index 26682b7..61ae328 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/gui/analysis/CorrelationTemplatesSelectionPanel.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/gui/analysis/CorrelationTemplatesSelectionPanel.java @@ -6,6 +6,7 @@ import com.blazemeter.jmeter.correlation.core.templates.CorrelationTemplatesRepositoriesConfiguration; import com.blazemeter.jmeter.correlation.core.templates.Repository; import com.blazemeter.jmeter.correlation.core.templates.Template; +import com.blazemeter.jmeter.correlation.core.templates.Template.Builder; import com.blazemeter.jmeter.correlation.core.templates.TemplateVersion; import com.blazemeter.jmeter.correlation.core.templates.repository.Properties; import com.blazemeter.jmeter.correlation.core.templates.repository.RepositoryManager; @@ -22,10 +23,14 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.stream.Collectors; import javax.swing.BorderFactory; import javax.swing.BoxLayout; @@ -49,18 +54,21 @@ public class CorrelationTemplatesSelectionPanel extends WizardStepPanel implements ActionListener { private static final long serialVersionUID = 240L; - private static final Logger LOG - = LoggerFactory.getLogger(CorrelationTemplatesSelectionPanel.class); + private static final Logger LOG = LoggerFactory.getLogger( + CorrelationTemplatesSelectionPanel.class); private static final String BROWSE = "browse"; private static final String CONTINUE = "continue"; private static final String CANCEL = "cancel"; private static final String RELOAD = "reload"; + private static final String DRAFT_REPOSITORY_NAME = "Draft"; private final Analysis analysis = new Analysis(); private TemplatesSelectionTable selectionTable; private JEditorPane informationPane; private JTextField traceFilePath; private BiConsumer, String> startNonCorrelatedAnalysis; private JButton continueButton; + private Function buildTemplate; + private Template draftTemplate; public CorrelationTemplatesSelectionPanel(CorrelationWizard wizard) { super(wizard); @@ -103,35 +111,22 @@ private JPanel buildInfoAndReloadPanel() { informationLabel.setText( "Select which Correlation Template and the version that you want to apply:"); - JButton reloadTemplates = new SwingUtils.ButtonBuilder() - .withActionListener(this) - .withAction(RELOAD) - .withName("templateReloadButton") - .build(); + JButton reloadTemplates = new SwingUtils.ButtonBuilder().withActionListener(this) + .withAction(RELOAD).withName("templateReloadButton").build(); reloadTemplates.setText("Reload Templates"); reloadTemplates.setToolTipText("Reload the available Correlation Templates"); GroupLayout layout = new GroupLayout(infoAndReloadPanel); infoAndReloadPanel.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(informationLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, - GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(reloadTemplates) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(informationLabel) - .addComponent(reloadTemplates)) - .addContainerGap()) - ); + layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup( + layout.createSequentialGroup().addContainerGap().addComponent(informationLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, + GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addComponent(reloadTemplates) + .addContainerGap())); + layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup( + layout.createSequentialGroup().addContainerGap().addGroup( + layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(informationLabel).addComponent(reloadTemplates)).addContainerGap())); return infoAndReloadPanel; } @@ -147,24 +142,17 @@ private JPanel buildCorrelationRulesSelectionPanel() { GroupLayout layout = new GroupLayout(correlationRulesSelectionPanel); correlationRulesSelectionPanel.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addComponent(correlationRulesTableScrollPane, GroupLayout.DEFAULT_SIZE, 400, - Short.MAX_VALUE)) - .addContainerGap())); - - layout.setVerticalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(correlationRulesTableScrollPane, GroupLayout.DEFAULT_SIZE, 200, - Short.MAX_VALUE) - .addContainerGap()) - ); + layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup( + layout.createSequentialGroup().addContainerGap().addGroup( + layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(correlationRulesTableScrollPane, GroupLayout.DEFAULT_SIZE, 400, + Short.MAX_VALUE)).addContainerGap())); + + layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup( + layout.createSequentialGroup().addContainerGap() + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(correlationRulesTableScrollPane, GroupLayout.DEFAULT_SIZE, 200, + Short.MAX_VALUE).addContainerGap())); return correlationRulesSelectionPanel; } @@ -181,9 +169,8 @@ private void onTemplateVersionFocus(Template focusedVersion) { TemplateSelectionTableModel model = (TemplateSelectionTableModel) selectionTable.getModel(); boolean canUse = model.canUseTemplate(focusedVersion); - informationPane.setText(TemplateVersionUtils - .getInformationAsHTLM(focusedVersion, false, canUse, - model.getRepositoryDisplayName(focusedVersion.getRepositoryId()))); + informationPane.setText(TemplateVersionUtils.getInformationAsHTLM(focusedVersion, false, canUse, + model.getRepositoryDisplayName(focusedVersion.getRepositoryId()))); informationPane.setCaretPosition(0); // Scroll to the top } @@ -197,10 +184,33 @@ public void loadPanel() { public void loadCorrelationTemplates() { Map repositoryMap = this.wizard.getRepositoriesSupplier().get(); + addDraftTemplate(repositoryMap); selectionTable.setRepositories(repositoryMap); selectionTable.selectFirstRow(); } + private void addDraftTemplate(Map repositoryMap) { + Repository draft = new Repository(DRAFT_REPOSITORY_NAME); + draft.setDisplayName(DRAFT_REPOSITORY_NAME); + initDraftTemplate(); + draft.addTemplate(draftTemplate, new TemplateProperties()); + repositoryMap.put(DRAFT_REPOSITORY_NAME, draft); + } + + private void initDraftTemplate() { + String lastModifiedDate = LocalDateTime.now() + .format(DateTimeFormatter.ofPattern("MM.dd.yyyy-HH.mm.ss")); + this.draftTemplate = buildTemplate.apply( + new Builder().withId("draft").withRepositoryId(DRAFT_REPOSITORY_NAME) + .withVersion("N/A") + .withChanges("Rules extracted on: " + lastModifiedDate) + .withDescription("These are the rules which are under development in Correlation Panel") + .withAuthor(System.getProperty("user.name") + "(you)") + .withUrl("") + .withDependencies(Collections.emptyList())); + + } + private JPanel buildCorrelationRulesInformationPanel() { JPanel correlationRulesInformationPanel = new JPanel(); correlationRulesInformationPanel.setBorder(BorderFactory.createEmptyBorder()); @@ -221,21 +231,15 @@ private JPanel buildCorrelationRulesInformationPanel() { GroupLayout layout = new GroupLayout(correlationRulesInformationPanel); correlationRulesInformationPanel.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - .addContainerGap()) - ); - - layout.setVerticalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 250, Short.MAX_VALUE) - .addContainerGap()) - ); + layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup( + layout.createSequentialGroup().addContainerGap() + .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) + .addContainerGap())); + + layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup( + layout.createSequentialGroup().addContainerGap() + .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 250, Short.MAX_VALUE) + .addContainerGap())); return correlationRulesInformationPanel; } @@ -252,11 +256,10 @@ private JPanel buildButtonsPanel() { traceFilePanel.add(traceFilePath); traceFilePanel.add( builder.withAction(BROWSE).withText("Browse").withName("templateBrowseButton").build()); - JButton cancelButton = - builder.withAction(CANCEL).withText("Cancel").withName("templateCancelButton").build(); - continueButton = - builder.withAction(CONTINUE).withText("Continue").withName("templateContinueButton") - .build(); + JButton cancelButton = builder.withAction(CANCEL).withText("Cancel") + .withName("templateCancelButton").build(); + continueButton = builder.withAction(CONTINUE).withText("Continue") + .withName("templateContinueButton").build(); enableContinue(false); JPanel buttonPanel = new JPanel(new FlowLayout()); @@ -294,8 +297,8 @@ private void reloadCorrelationTemplates() { UpdateRepositoriesWorker worker = new UpdateRepositoriesWorker() { @Override protected Boolean doInBackground() { - return wizard.getRepositoriesConfiguration().getLocalConfig().refreshRepositories("", - this::setProgress, this::publish); + return wizard.getRepositoriesConfiguration().getLocalConfig() + .refreshRepositories("", this::setProgress, this::publish); } }; @@ -326,30 +329,34 @@ private void validateAndContinue() { } private void onContinue() { - Map> repositoryGrouped - = selectionTable.getSelectedTemplateWithRepositoryMap(); + Map> repositoryGrouped = + selectionTable.getSelectedTemplateWithRepositoryMap(); + boolean isDraft = false; List