Index: src/core/org/apache/jmeter/reporters/ResultSaver.java =================================================================== --- src/core/org/apache/jmeter/reporters/ResultSaver.java (revision 1826463) +++ src/core/org/apache/jmeter/reporters/ResultSaver.java (working copy) @@ -28,6 +28,7 @@ import java.text.SimpleDateFormat; import java.util.Date; +import org.apache.jmeter.control.TransactionController; import org.apache.jmeter.engine.util.NoThreadClone; import org.apache.jmeter.samplers.SampleEvent; import org.apache.jmeter.samplers.SampleListener; @@ -36,6 +37,7 @@ import org.apache.jmeter.testelement.AbstractTestElement; import org.apache.jmeter.testelement.TestStateListener; import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jorphan.util.JMeterStopTestNowException; import org.apache.jorphan.util.JOrphanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -73,6 +75,8 @@ public static final String ADD_TIMESTAMP = "FileSaver.addTimstamp"; // $NON-NLS-1$ public static final String NUMBER_PAD_LENGTH = "FileSaver.numberPadLen"; // $NON-NLS-1$ + + public static final String IGNORE_TC = "FileSaver.ignoreTC"; // $NON-NLS-1$ //- JMX property names @@ -150,7 +154,7 @@ */ @Override public void sampleOccurred(SampleEvent e) { - processSample(e.getResult(), new Counter()); + processSample(e.getResult(), new Counter()); } /** @@ -172,15 +176,11 @@ * @param num number to append to variable (if >0) */ private void saveSample(SampleResult s, int num) { - // Should we save the sample? - if (s.isSuccessful()){ - if (getErrorsOnly()){ - return; + if(ignoreSampler(s)) { + if (log.isDebugEnabled()) { + log.debug("Ignoring SampleResult from Sampler {}", s.getSampleLabel()); } - } else { - if (getSuccessOnly()){ - return; - } + return; } String fileName = makeFileName(s.getContentType(), getSkipAutoNumber(), getSkipSuffix()); @@ -198,6 +198,7 @@ JMeterContextService.getContext().getVariables().put(variable, fileName); } File out = new File(fileName); + createFoldersIfNeeded(out.getParentFile()); try (FileOutputStream fos = new FileOutputStream(out); BufferedOutputStream bos = new BufferedOutputStream(fos)){ JOrphanUtils.write(s.getResponseData(), bos); // chunk the output if necessary @@ -209,6 +210,34 @@ } /** + * @param s {@link SamplerResult} + * @return true if we should ignore SampleResult + */ + private boolean ignoreSampler(SampleResult s) { + if(getIgnoreTC() && TransactionController.isFromTransactionController(s)) { + return true; + } + // Should we save the sample? + return ((s.isSuccessful() && getErrorsOnly()) || + (!s.isSuccessful() && getSuccessOnly())); + } + + /** + * Create path hierarchy to parentFile + * @param parentFile + */ + private void createFoldersIfNeeded(File parentFile) { + if (!parentFile.exists()) { + log.debug("Creating path hierarchy for folder {}", parentFile.getAbsolutePath()); + if(!parentFile.mkdirs()) { + throw new JMeterStopTestNowException("Cannot create path hierarchy for folder "+ parentFile.getAbsolutePath()); + } + } else { + log.debug("Folder {} already exists", parentFile.getAbsolutePath()); + } + } + + /** * @param contentType Content type * @param skipAutoNumber Skip auto number * @param skipSuffix Skip suffix @@ -263,37 +292,49 @@ // not used } - private String getFilename() { + public String getFilename() { return getPropertyAsString(FILENAME); } - private String getVariableName() { + public String getVariableName() { return getPropertyAsString(VARIABLE_NAME,""); // $NON-NLS-1$ } - private boolean getErrorsOnly() { + public boolean getErrorsOnly() { return getPropertyAsBoolean(ERRORS_ONLY); } - private boolean getSkipAutoNumber() { + public boolean getSkipAutoNumber() { return getPropertyAsBoolean(SKIP_AUTO_NUMBER); } - private boolean getSkipSuffix() { + public boolean getSkipSuffix() { return getPropertyAsBoolean(SKIP_SUFFIX); } - private boolean getSuccessOnly() { + public boolean getSuccessOnly() { return getPropertyAsBoolean(SUCCESS_ONLY); } - private boolean getAddTimeStamp() { + public boolean getAddTimeStamp() { return getPropertyAsBoolean(ADD_TIMESTAMP); } - private int getNumberPadLen() { + public int getNumberPadLen() { return getPropertyAsInt(NUMBER_PAD_LENGTH, 0); } + + public boolean getIgnoreTC() { + return getPropertyAsBoolean(IGNORE_TC, true); + } + + public void setIgnoreTC(boolean value) { + setProperty(IGNORE_TC, value, true); + } + + public void setFilename(String value) { + setProperty(FILENAME, value); + } // Mutable int to keep track of sample count private static class Counter{ @@ -315,4 +356,32 @@ public boolean equals(Object obj) { return super.equals(obj); } + + public void setAddTimestamp(boolean selected) { + setProperty(ADD_TIMESTAMP, selected, false); + } + + public void setVariableName(String value) { + setProperty(VARIABLE_NAME, value,""); //$NON-NLS-1$ + } + + public void setNumberPadLength(String text) { + setProperty(ResultSaver.NUMBER_PAD_LENGTH, text,""); //$NON-NLS-1$ + } + + public void setErrorsOnly(boolean selected) { + setProperty(ResultSaver.ERRORS_ONLY, selected); + } + + public void setSuccessOnly(boolean selected) { + setProperty(ResultSaver.SUCCESS_ONLY, selected); + } + + public void setSkipSuffix(boolean selected) { + setProperty(ResultSaver.SKIP_SUFFIX, selected); + } + + public void setSkipAutoNumber(boolean selected) { + setProperty(ResultSaver.SKIP_AUTO_NUMBER, selected); + } } Index: src/core/org/apache/jmeter/reporters/gui/ResultSaverGui.java =================================================================== --- src/core/org/apache/jmeter/reporters/gui/ResultSaverGui.java (revision 1826463) +++ src/core/org/apache/jmeter/reporters/gui/ResultSaverGui.java (working copy) @@ -19,16 +19,19 @@ package org.apache.jmeter.reporters.gui; import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.List; +import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JCheckBox; -import javax.swing.JLabel; +import javax.swing.JComponent; import javax.swing.JPanel; -import javax.swing.JTextField; import org.apache.jmeter.reporters.ResultSaver; import org.apache.jmeter.samplers.Clearable; -import org.apache.jmeter.testelement.AbstractTestElement; import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.util.JMeterUtils; import org.apache.jmeter.visualizers.gui.AbstractListenerGui; @@ -39,26 +42,28 @@ * of files * */ -public class ResultSaverGui extends AbstractListenerGui implements Clearable { +public class ResultSaverGui extends AbstractListenerGui implements Clearable { // NOSONAR Ignore inheritance rule - private static final long serialVersionUID = 240L; + private static final long serialVersionUID = 241L; - private JTextField filename; + private JLabeledTextField filename; - private JTextField variableName; + private JLabeledTextField variableName; + + private JLabeledTextField numberPadLength; private JCheckBox errorsOnly; private JCheckBox successOnly; + private JCheckBox ignoreTC; + private JCheckBox skipAutoNumber; private JCheckBox skipSuffix; private JCheckBox addTimestamp; - private JLabeledTextField numberPadLength; - public ResultSaverGui() { super(); init(); @@ -78,14 +83,17 @@ @Override public void configure(TestElement el) { super.configure(el); - filename.setText(el.getPropertyAsString(ResultSaver.FILENAME)); - errorsOnly.setSelected(el.getPropertyAsBoolean(ResultSaver.ERRORS_ONLY)); - successOnly.setSelected(el.getPropertyAsBoolean(ResultSaver.SUCCESS_ONLY)); - skipAutoNumber.setSelected(el.getPropertyAsBoolean(ResultSaver.SKIP_AUTO_NUMBER)); - skipSuffix.setSelected(el.getPropertyAsBoolean(ResultSaver.SKIP_SUFFIX)); - variableName.setText(el.getPropertyAsString(ResultSaver.VARIABLE_NAME,"")); - addTimestamp.setSelected(el.getPropertyAsBoolean(ResultSaver.ADD_TIMESTAMP)); - numberPadLength.setText(el.getPropertyAsString(ResultSaver.NUMBER_PAD_LENGTH,"")); + ResultSaver resultSaver = (ResultSaver) el; + filename.setText(resultSaver.getFilename()); + errorsOnly.setSelected(resultSaver.getErrorsOnly()); + successOnly.setSelected(resultSaver.getSuccessOnly()); + ignoreTC.setSelected(resultSaver.getIgnoreTC()); + skipAutoNumber.setSelected(resultSaver.getSkipAutoNumber()); + skipSuffix.setSelected(resultSaver.getSkipSuffix()); + variableName.setText(resultSaver.getVariableName()); + addTimestamp.setSelected(resultSaver.getAddTimeStamp()); + numberPadLength.setText(resultSaver.getNumberPadLen() == 0 ? + "" : Integer.toString(resultSaver.getNumberPadLen())); } /** @@ -106,15 +114,16 @@ @Override public void modifyTestElement(TestElement te) { super.configureTestElement(te); - te.setProperty(ResultSaver.FILENAME, filename.getText()); - te.setProperty(ResultSaver.ERRORS_ONLY, errorsOnly.isSelected()); - te.setProperty(ResultSaver.SKIP_AUTO_NUMBER, skipAutoNumber.isSelected()); - te.setProperty(ResultSaver.SKIP_SUFFIX, skipSuffix.isSelected()); - te.setProperty(ResultSaver.SUCCESS_ONLY, successOnly.isSelected()); - te.setProperty(ResultSaver.ADD_TIMESTAMP, addTimestamp.isSelected(), false); - AbstractTestElement at = (AbstractTestElement) te; - at.setProperty(ResultSaver.VARIABLE_NAME, variableName.getText(),""); //$NON-NLS-1$ - at.setProperty(ResultSaver.NUMBER_PAD_LENGTH, numberPadLength.getText(),""); //$NON-NLS-1$ + ResultSaver resultSaver = (ResultSaver) te; + resultSaver.setFilename(filename.getText()); + resultSaver.setErrorsOnly(errorsOnly.isSelected()); + resultSaver.setSuccessOnly(successOnly.isSelected()); + resultSaver.setSkipSuffix(skipSuffix.isSelected()); + resultSaver.setSkipAutoNumber(skipAutoNumber.isSelected()); + resultSaver.setIgnoreTC(ignoreTC.isSelected()); + resultSaver.setAddTimestamp(addTimestamp.isSelected()); + resultSaver.setVariableName(variableName.getText()); + resultSaver.setNumberPadLength(numberPadLength.getText()); } /** @@ -129,6 +138,7 @@ filename.setText(""); //$NON-NLS-1$ errorsOnly.setSelected(false); successOnly.setSelected(false); + ignoreTC.setSelected(true); addTimestamp.setSelected(false); variableName.setText(""); //$NON-NLS-1$ numberPadLength.setText(""); //$NON-NLS-1$ @@ -137,57 +147,107 @@ private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final) setLayout(new BorderLayout()); setBorder(makeBorder()); + Box box = Box.createVerticalBox(); box.add(makeTitlePanel()); - box.add(createFilenamePrefixPanel()); - box.add(createVariableNamePanel()); - errorsOnly = new JCheckBox(JMeterUtils.getResString("resultsaver_errors")); // $NON-NLS-1$ - box.add(errorsOnly); - successOnly = new JCheckBox(JMeterUtils.getResString("resultsaver_success")); // $NON-NLS-1$ - box.add(successOnly); - skipAutoNumber = new JCheckBox(JMeterUtils.getResString("resultsaver_skipautonumber")); // $NON-NLS-1$ - box.add(skipAutoNumber); - skipSuffix = new JCheckBox(JMeterUtils.getResString("resultsaver_skipsuffix")); // $NON-NLS-1$ - box.add(skipSuffix); - addTimestamp = new JCheckBox(JMeterUtils.getResString("resultsaver_addtimestamp")); // $NON-NLS-1$ - box.add(addTimestamp); - numberPadLength = new JLabeledTextField(JMeterUtils.getResString("resultsaver_numberpadlen"));// $NON-NLS-1$ - box.add(numberPadLength); + box.add(createSaveConditionsPanel()); + box.add(createSaveFormatPanel()); add(box, BorderLayout.NORTH); } - private JPanel createFilenamePrefixPanel() - { - JLabel label = new JLabel(JMeterUtils.getResString("resultsaver_prefix")); // $NON-NLS-1$ - - filename = new JTextField(10); + private Component createSaveFormatPanel() { + filename = new JLabeledTextField(JMeterUtils.getResString("resultsaver_prefix")); filename.setName(ResultSaver.FILENAME); - label.setLabelFor(filename); - - JPanel filenamePanel = new JPanel(new BorderLayout(5, 0)); - filenamePanel.add(label, BorderLayout.WEST); - filenamePanel.add(filename, BorderLayout.CENTER); - return filenamePanel; - } + numberPadLength = new JLabeledTextField(JMeterUtils.getResString("resultsaver_numberpadlen"));// $NON-NLS-1$ + numberPadLength.setName(ResultSaver.NUMBER_PAD_LENGTH); - private JPanel createVariableNamePanel() - { - JLabel label = new JLabel(JMeterUtils.getResString("resultsaver_variable")); // $NON-NLS-1$ + skipAutoNumber = new JCheckBox(JMeterUtils.getResString("resultsaver_skipautonumber")); // $NON-NLS-1$ + skipSuffix = new JCheckBox(JMeterUtils.getResString("resultsaver_skipsuffix")); // $NON-NLS-1$ + addTimestamp = new JCheckBox(JMeterUtils.getResString("resultsaver_addtimestamp")); // $NON-NLS-1$ - variableName = new JTextField(10); + variableName = new JLabeledTextField(JMeterUtils.getResString("resultsaver_variable")); variableName.setName(ResultSaver.VARIABLE_NAME); - label.setLabelFor(variableName); + + JPanel panel = new JPanel(new GridBagLayout()); + panel.setBorder(BorderFactory.createTitledBorder(JMeterUtils.getResString("resultsaver_save_format"))); //$NON-NLS-1$ + GridBagConstraints gbc = new GridBagConstraints(); + initConstraints(gbc); + + addField(panel, variableName, gbc); + resetContraints(gbc); + addField(panel, filename, gbc); + resetContraints(gbc); + addField(panel, skipAutoNumber, gbc); + resetContraints(gbc); + addField(panel, skipSuffix, gbc); + resetContraints(gbc); + addField(panel, addTimestamp, gbc); + resetContraints(gbc); + addField(panel, numberPadLength, gbc); + resetContraints(gbc); - JPanel filenamePanel = new JPanel(new BorderLayout(5, 0)); - filenamePanel.add(label, BorderLayout.WEST); - filenamePanel.add(variableName, BorderLayout.CENTER); - return filenamePanel; + return panel; } + private Component createSaveConditionsPanel() { + successOnly = new JCheckBox(JMeterUtils.getResString("resultsaver_success")); // $NON-NLS-1$ + errorsOnly = new JCheckBox(JMeterUtils.getResString("resultsaver_errors")); // $NON-NLS-1$ + ignoreTC = new JCheckBox(JMeterUtils.getResString("resultsaver_ignore_tc")); // $NON-NLS-1$ + + JPanel panel = new JPanel(new GridBagLayout()); + panel.setBorder(BorderFactory.createTitledBorder(JMeterUtils.getResString("resultsaver_save_conditions"))); //$NON-NLS-1$ + GridBagConstraints gbc = new GridBagConstraints(); + initConstraints(gbc); + + addField(panel, successOnly, gbc); + resetContraints(gbc); + addField(panel, errorsOnly, gbc); + resetContraints(gbc); + addField(panel, ignoreTC, gbc); + resetContraints(gbc); + + return panel; + } + // Needed to avoid Class cast error in Clear.java @Override public void clearData() { + // NOOP + } + + private void addField(JPanel panel, JCheckBox field, GridBagConstraints gbc) { + gbc.weightx = 2; + gbc.fill=GridBagConstraints.HORIZONTAL; + panel.add(field, gbc.clone()); + } + + private void addField(JPanel panel, JLabeledTextField field, GridBagConstraints gbc) { + List item = field.getComponentList(); + panel.add(item.get(0), gbc.clone()); + gbc.gridx++; + gbc.weightx = 1; + gbc.fill=GridBagConstraints.HORIZONTAL; + panel.add(item.get(1), gbc.clone()); + } + + // Next line + private void resetContraints(GridBagConstraints gbc) { + gbc.gridx = 0; + gbc.gridy++; + gbc.weightx = 0; + gbc.fill=GridBagConstraints.NONE; + } + + private void initConstraints(GridBagConstraints gbc) { + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.fill = GridBagConstraints.NONE; + gbc.gridheight = 1; + gbc.gridwidth = 1; + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 0; + gbc.weighty = 0; } } Index: src/core/org/apache/jmeter/resources/messages.properties =================================================================== --- src/core/org/apache/jmeter/resources/messages.properties (revision 1826463) +++ src/core/org/apache/jmeter/resources/messages.properties (working copy) @@ -939,13 +939,16 @@ resultaction_title=Result Status Action Handler resultsaver_addtimestamp=Add timestamp resultsaver_errors=Save Failed Responses only -resultsaver_numberpadlen=Minimum Length of sequence number -resultsaver_prefix=Filename prefix\: +resultsaver_ignore_tc=Don't save Transaction Controller SampleResult +resultsaver_numberpadlen=Minimum Length of sequence number \: +resultsaver_prefix=Filename prefix (can include folders) \: +resultsaver_save_conditions=Save conditions +resultsaver_save_format=Save details resultsaver_skipautonumber=Don't add number to prefix -resultsaver_skipsuffix=Don't add suffix +resultsaver_skipsuffix=Don't add content type suffix resultsaver_success=Save Successful Responses only resultsaver_title=Save Responses to a file -resultsaver_variable=Variable Name: +resultsaver_variable=Variable Name containing saved file name \: result_function=The result of the function is retobj=Return object return_code_config_box_title=Return Code Configuration Index: src/core/org/apache/jmeter/resources/messages_fr.properties =================================================================== --- src/core/org/apache/jmeter/resources/messages_fr.properties (revision 1826463) +++ src/core/org/apache/jmeter/resources/messages_fr.properties (working copy) @@ -929,8 +929,11 @@ resultaction_title=Op\u00E9rateur R\u00E9sultats Action resultsaver_addtimestamp=Ajouter un timestamp resultsaver_errors=Enregistrer seulement les r\u00E9ponses en \u00E9checs +resultsaver_ignore_tc=Ne pas enregistrer l''\u00E9chantillon Transaction Controller resultsaver_numberpadlen=Taille minimale du num\u00E9ro de s\u00E9quence -resultsaver_prefix=Pr\u00E9fixe du nom de fichier \: +resultsaver_prefix=Pr\u00E9fixe du nom de fichier \: +resultsaver_save_conditions=Conditions d''enregistrement +resultsaver_save_format=D\u00E9tails de l''enregistrement resultsaver_skipautonumber=Ne pas ajouter de nombre au pr\u00E9fixe resultsaver_skipsuffix=Ne pas ajouter de suffixe resultsaver_success=Enregistrer seulement les r\u00E9ponses en succ\u00E8s Index: xdocs/usermanual/component_reference.xml =================================================================== --- xdocs/usermanual/component_reference.xml (revision 1826463) +++ xdocs/usermanual/component_reference.xml (working copy) @@ -3178,23 +3178,26 @@ Descriptive name for this element that is shown in the tree. - Prefix for the generated file names; this can include a directory name. + Prefix for the generated file names; this can include a directory name. Relative paths are resolved relative to the current working directory (which defaults to the bin/ directory). JMeter also supports paths relative to the directory containing the current test plan (JMX file). If the path name begins with "~/" (or whatever is in the jmeter.save.saveservice.base_prefix JMeter property), - then the path is assumed to be relative to the JMX file location. + then the path is assumed to be relative to the JMX file location.
+ If parent folders in prefix do not exists, JMeter will create them and stop test if it fails.
- + Name of a variable in which to save the generated file name (so it can be used later in the test plan). If there are sub-samples then a numeric suffix is added to the variable name. E.g. if the variable name is FILENAME, then the parent sample file name is saved in the variable FILENAME, and the filenames for the child samplers are saved in FILENAME1, FILENAME2 etc. + If "Don't add number to prefix" is not checked, then numbers added to prefix will be padded by 0 so that prefix is has size of this value. Defaults to 0. If selected, then only failed responses are saved If selected, then only successful responses are saved If selected, then no number is added to the prefix. If you select this option, make sure that the prefix is unique or the file may be overwritten. - If selected, then no suffix is added. If you select this option, make sure that the prefix is unique or the file may be overwritten. - If "Don't add number to prefix" is not checked, then numbers added to prefix will be padded by 0 so that prefix is has size of this value. Defaults to 0. + If selected, then no suffix is added. If you select this option, make sure that the prefix is unique or the file may be overwritten. + If selected, then date will be included in file suffix following format yyyyMMdd-HHmm_ + If selected, then SamplerResult generated by Transaction Controller will be ignored