jmeter-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pmoua...@apache.org
Subject svn commit: r1847594 - in /jmeter/trunk: ./ bin/templates/ src/core/org/apache/jmeter/gui/action/ src/core/org/apache/jmeter/gui/action/template/ src/core/org/apache/jmeter/resources/ src/core/org/apache/jmeter/util/ test/resources/org/apache/jmeter/gu...
Date Tue, 27 Nov 2018 20:23:45 GMT
Author: pmouawad
Date: Tue Nov 27 20:23:44 2018
New Revision: 1847594

URL: http://svn.apache.org/viewvc?rev=1847594&view=rev
Log:
Bug 62870 - Templates : Add ability to provide parameters 

Contributed by UbikLoadPack (https://ubikloadpack.com)

This closes #432


Bugzilla Id: 62870

Added:
    jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/templates.dtd   (with props)
    jmeter/trunk/src/core/org/apache/jmeter/util/TemplateUtil.java   (with props)
    jmeter/trunk/test/resources/org/apache/jmeter/gui/
    jmeter/trunk/test/resources/org/apache/jmeter/gui/action/
    jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/
    jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/invalidTemplates.xml   (with props)
    jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/validTemplates.xml   (with props)
    jmeter/trunk/test/src/org/apache/jmeter/gui/action/template/
    jmeter/trunk/test/src/org/apache/jmeter/gui/action/template/TestTemplateManager.java   (with props)
    jmeter/trunk/xdocs/creating-templates.xml   (with props)
    jmeter/trunk/xdocs/images/screenshots/templates/
    jmeter/trunk/xdocs/images/screenshots/templates/template_folder.png   (with props)
    jmeter/trunk/xdocs/images/screenshots/templates/template_parameters.png   (with props)
    jmeter/trunk/xdocs/images/screenshots/templates/template_parameters_window.png   (with props)
    jmeter/trunk/xdocs/images/screenshots/templates/template_recording_custom_filename.png   (with props)
    jmeter/trunk/xdocs/images/screenshots/templates/template_recording_filename.png   (with props)
    jmeter/trunk/xdocs/images/screenshots/templates/template_recording_retrieved_value.png   (with props)
    jmeter/trunk/xdocs/images/screenshots/templates/templates_xml_parameters.png   (with props)
Modified:
    jmeter/trunk/bin/templates/recording.jmx
    jmeter/trunk/bin/templates/templates.dtd
    jmeter/trunk/bin/templates/templates.xml
    jmeter/trunk/build.xml
    jmeter/trunk/src/core/org/apache/jmeter/gui/action/SelectTemplatesDialog.java
    jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/Template.java
    jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/TemplateManager.java
    jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties
    jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties
    jmeter/trunk/xdocs/changes.xml

Modified: jmeter/trunk/bin/templates/recording.jmx
URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/templates/recording.jmx?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/bin/templates/recording.jmx (original)
+++ jmeter/trunk/bin/templates/recording.jmx Tue Nov 27 20:23:44 2018
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<jmeterTestPlan version="1.2" properties="4.0" jmeter="4.0-SNAPSHOT.20180218">
+<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.1-SNAPSHOT.20181118">
   <hashTree>
     <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
       <stringProp name="TestPlan.comments"></stringProp>
@@ -12,16 +12,27 @@
     </TestPlan>
     <hashTree>
       <Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
-        <collectionProp name="Arguments.arguments"/>
+        <collectionProp name="Arguments.arguments">
+          <elementProp name="host" elementType="Argument">
+            <stringProp name="Argument.name">host</stringProp>
+            <stringProp name="Argument.value">[=hostToRecord]</stringProp>
+            <stringProp name="Argument.metadata">=</stringProp>
+          </elementProp>
+          <elementProp name="scheme" elementType="Argument">
+            <stringProp name="Argument.name">scheme</stringProp>
+            <stringProp name="Argument.value">[=schemeToRecord]</stringProp>
+            <stringProp name="Argument.metadata">=</stringProp>
+          </elementProp>
+        </collectionProp>
       </Arguments>
       <hashTree/>
       <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">
         <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
           <collectionProp name="Arguments.arguments"/>
         </elementProp>
-        <stringProp name="HTTPSampler.domain"></stringProp>
+        <stringProp name="HTTPSampler.domain">[=hostToRecord]</stringProp>
         <stringProp name="HTTPSampler.port"></stringProp>
-        <stringProp name="HTTPSampler.protocol"></stringProp>
+        <stringProp name="HTTPSampler.protocol">[=schemeToRecord]</stringProp>
         <stringProp name="HTTPSampler.contentEncoding"></stringProp>
         <stringProp name="HTTPSampler.path"></stringProp>
         <stringProp name="HTTPSampler.concurrentPool">6</stringProp>
@@ -90,9 +101,8 @@
       <ProxyControl guiclass="ProxyControlGui" testclass="ProxyControl" testname="HTTP(S) Test Script Recorder" enabled="false">
         <stringProp name="ProxyControlGui.port">8888</stringProp>
         <collectionProp name="ProxyControlGui.exclude_list">
-          <stringProp name="1301401588">.*toolbar\.live\.com.*</stringProp>
           <stringProp name="1179605444">(?i).*\.(bmp|css|js|gif|ico|jpe?g|png|swf|eot|otf|ttf|mp4|woff|woff2)</stringProp>
-          <stringProp name="1276958334">update\.microsoft\.com.*</stringProp>
+          <stringProp name="-88591710">www\.download\.windowsupdate\.com.*</stringProp>
           <stringProp name="195066122">toolbarqueries\.google\..*</stringProp>
           <stringProp name="-1570593883">clients.*\.google.*</stringProp>
           <stringProp name="339269285">api\.bing\.com.*</stringProp>
@@ -171,7 +181,7 @@
               <connectTime>true</connectTime>
             </value>
           </objProp>
-          <stringProp name="filename">recording.xml</stringProp>
+          <stringProp name="filename">[=recordingOutputFile]</stringProp>
         </ResultCollector>
         <hashTree/>
       </hashTree>

Modified: jmeter/trunk/bin/templates/templates.dtd
URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/templates/templates.dtd?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/bin/templates/templates.dtd (original)
+++ jmeter/trunk/bin/templates/templates.dtd Tue Nov 27 20:23:44 2018
@@ -15,10 +15,10 @@
    limitations under the License.
 -->
 
-<!--  Basic DTD for podlings.xml -->
+<!--  Basic DTD for templates.xml -->
 <!ELEMENT templates (template+)>
 
-<!ELEMENT template (name, fileName, description)>
+<!ELEMENT template (name, fileName, description, parameters?)>
 <!-- Whether the template is a complete test plan or not -->
 <!ATTLIST template isTestPlan (true|false) #REQUIRED>
 
@@ -27,3 +27,11 @@
 <!ELEMENT fileName  (#PCDATA)  >
 
 <!ELEMENT description  (#PCDATA)  >
+
+<!ELEMENT parameters (parameter*)>
+
+<!ELEMENT parameter EMPTY>
+
+<!ATTLIST parameter key CDATA #REQUIRED>
+
+<!ATTLIST parameter defaultValue CDATA #REQUIRED>

Modified: jmeter/trunk/bin/templates/templates.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/templates/templates.xml?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/bin/templates/templates.xml (original)
+++ jmeter/trunk/bin/templates/templates.xml Tue Nov 27 20:23:44 2018
@@ -18,7 +18,7 @@
 <!-- 
     See the DTD for allowable elements and attributes.
  -->
- <!DOCTYPE templates SYSTEM "templates.dtd">
+<!DOCTYPE templates SYSTEM "templates.dtd">
 <templates>
     <template isTestPlan="true">
         <name>Recording</name>
@@ -42,6 +42,11 @@
                 <li><a href="http://jmeter.apache.org/usermanual/component_reference.html#HTTP_Proxy_Server" >http://jmeter.apache.org/usermanual/component_reference.html#HTTP_Proxy_Server</a></li>
             </ul>       
         ]]></description>
+        <parameters>
+            <parameter defaultValue="recording.xml" key="recordingOutputFile"/>
+            <parameter defaultValue="www.example.com" key="hostToRecord"/>
+            <parameter defaultValue="https" key="schemeToRecord"/>
+        </parameters>
     </template>
     <template isTestPlan="true">
         <name>Recording with Think Time</name>

Modified: jmeter/trunk/build.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/build.xml?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/build.xml (original)
+++ jmeter/trunk/build.xml Tue Nov 27 20:23:44 2018
@@ -1057,8 +1057,8 @@ run JMeter unless all the JMeter jars ar
       <fileset dir="${src.core}" includes="**/*.properties">
         <exclude name="*eucJP*"/>
       </fileset>
-      <fileset dir="${src.core}" includes="**/*.xml">
-      </fileset>
+      <fileset dir="${src.core}" includes="**/*.xml" />
+      <fileset dir="${src.core}" includes="**/*.dtd" />
       <!-- This file is used by the jmeter -h option -->
       <fileset dir="${src.core}" includes="org/apache/jmeter/help.txt"/>
     </jar>
@@ -1703,6 +1703,7 @@ run JMeter unless all the JMeter jars ar
       <zipfileset dir="${dest.jar.jmeter}" prefix="bin" includes="log4j2.xml"/>
       <zipfileset dir="${dest.jar.jmeter}" prefix="bin" includes="proxyserver.jks"/>
       <zipfileset dir="${dest.jar.jmeter}" prefix="bin" includes="users.dtd"/>
+      <zipfileset dir="${dest.jar.jmeter}" prefix="bin" includes="templates/templates.dtd"/>
       <zipfileset dir="${dest.jar.jmeter}" prefix="bin" includes="users.xml"/>
       <zipfileset dir="${dest.jar.jmeter}" prefix="bin" includes="report-template/**/*.*" />
     </jar>

Modified: jmeter/trunk/src/core/org/apache/jmeter/gui/action/SelectTemplatesDialog.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/gui/action/SelectTemplatesDialog.java?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/gui/action/SelectTemplatesDialog.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/gui/action/SelectTemplatesDialog.java Tue Nov 27 20:23:44 2018
@@ -22,10 +22,20 @@ import java.awt.BorderLayout;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
 import java.awt.HeadlessException;
+import java.awt.Insets;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
 import java.io.File;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import javax.swing.AbstractAction;
 import javax.swing.Action;
@@ -35,6 +45,7 @@ import javax.swing.JButton;
 import javax.swing.JComponent;
 import javax.swing.JDialog;
 import javax.swing.JFrame;
+import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JRootPane;
@@ -50,16 +61,21 @@ import org.apache.jmeter.gui.action.temp
 import org.apache.jmeter.gui.action.template.TemplateManager;
 import org.apache.jmeter.swing.HtmlPane;
 import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jmeter.util.TemplateUtil;
 import org.apache.jorphan.gui.ComponentUtil;
 import org.apache.jorphan.gui.JLabeledChoice;
+import org.apache.jorphan.gui.JLabeledTextField;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import freemarker.template.Configuration;
+import freemarker.template.TemplateException;
+
 /**
  * Dialog used for Templates selection
  * @since 2.10
  */
-public class SelectTemplatesDialog extends JDialog implements ChangeListener, ActionListener, HyperlinkListener {
+public class SelectTemplatesDialog extends JDialog implements ChangeListener, ActionListener, HyperlinkListener { // NOSONAR Ignore inheritence warning
 
     private static final long serialVersionUID = 1;
     
@@ -83,7 +99,13 @@ public class SelectTemplatesDialog exten
 
     private final JButton cancelButton = new JButton(JMeterUtils.getResString("cancel")); //$NON-NLS-1$
     
-    private final JScrollPane scroller = new JScrollPane(helpDoc);
+    private final JButton previous = new JButton(JMeterUtils.getResString("previous")); //$NON-NLS-1$
+    
+    private final JButton validateButton = new JButton(JMeterUtils.getResString("validate_threadgroup")); //$NON-NLS-1$
+    
+    private Map<String, JLabeledTextField> parametersTextFields = new LinkedHashMap<>();
+    
+    private JPanel actionBtnBar = new JPanel(new FlowLayout());
 
     public SelectTemplatesDialog() {
         super((JFrame) null, JMeterUtils.getResString("template_title"), true); //$NON-NLS-1$
@@ -127,7 +149,8 @@ public class SelectTemplatesDialog exten
     
     /**
      * Check if existing Test Plan has been modified and ask user 
-     * what he wants to do if test plan is dirty
+     * what he wants to do if test plan is dirty. 
+     * Also ask user for parameters in case of customizable templates.
      * @param actionEvent {@link ActionEvent}
      */
     private void checkDirtyAndLoad(final ActionEvent actionEvent)
@@ -137,67 +160,138 @@ public class SelectTemplatesDialog exten
         if (template == null) {
             return;
         }
+        templateList.setValues(TemplateManager.getInstance().reset().getTemplateNames()); // reload the templates before loading
+        
         final boolean isTestPlan = template.isTestPlan();
         // Check if the user wants to drop any changes
-        if (isTestPlan) {
-            ActionRouter.getInstance().doActionNow(new ActionEvent(actionEvent.getSource(), actionEvent.getID(), ActionNames.CHECK_DIRTY));
-            GuiPackage guiPackage = GuiPackage.getInstance();
-            if (guiPackage.isDirty()) {
-                // Check if the user wants to create from template
-                int response = JOptionPane.showConfirmDialog(GuiPackage.getInstance().getMainFrame(),
-                        JMeterUtils.getResString("cancel_new_from_template"), // $NON-NLS-1$
-                        JMeterUtils.getResString("template_load?"),  // $NON-NLS-1$
-                        JOptionPane.YES_NO_CANCEL_OPTION,
-                        JOptionPane.QUESTION_MESSAGE);
-                if(response == JOptionPane.YES_OPTION) {
-                    ActionRouter.getInstance().doActionNow(new ActionEvent(actionEvent.getSource(), actionEvent.getID(), ActionNames.SAVE));
-                }
-                if (response == JOptionPane.CLOSED_OPTION || response == JOptionPane.CANCEL_OPTION) {
-                    return; // Don't clear the plan
-                }
-            }
+        if (isTestPlan && !checkDirty(actionEvent)) {
+            return;
         }
         ActionRouter.getInstance().doActionNow(new ActionEvent(actionEvent.getSource(), actionEvent.getID(), ActionNames.STOP_THREAD));
         final File parent = template.getParent();
-        final File fileToCopy = parent != null 
+        File fileToCopy = parent != null 
               ? new File(parent, template.getFileName())
-              : new File(JMeterUtils.getJMeterHome(), template.getFileName());       
-        Load.loadProjectFile(actionEvent, fileToCopy, !isTestPlan, false);
-        this.setVisible(false);
+              : new File(JMeterUtils.getJMeterHome(), template.getFileName());
+        replaceTemplateParametersAndLoad(actionEvent, template, isTestPlan, fileToCopy);
+    }
+
+    /**
+     * @param actionEvent {@link ActionEvent}
+     * @param template {@link Template} definition
+     * @param isTestPlan If it's a full test plan or a part
+     * @param templateFile Template file to load
+     */
+    void replaceTemplateParametersAndLoad(final ActionEvent actionEvent, final Template template,
+            final boolean isTestPlan, File templateFile) {
+        File temporaryGeneratedFile = null;
+        try {
+            // handle customized templates (the .jmx.fmkr files)
+            if (template.getParameters() != null && !template.getParameters().isEmpty()) {
+                File jmxFile = new File(templateFile.getAbsolutePath());
+                Map<String, String> userParameters = getUserParameters();
+                Configuration templateCfg = TemplateUtil.getTemplateConfig();
+                try {
+                    temporaryGeneratedFile = File.createTempFile(template.getName(), ".output");
+                    templateFile = temporaryGeneratedFile;
+                    TemplateUtil.processTemplate(jmxFile, temporaryGeneratedFile, templateCfg, userParameters);
+                } catch (IOException | TemplateException ex) {
+                    log.error("Error generating output file {} from template {}", temporaryGeneratedFile, jmxFile, ex);
+                    return;
+                }
+            }
+            Load.loadProjectFile(actionEvent, templateFile, !isTestPlan, false);
+            this.dispose();
+        } finally {
+            if (temporaryGeneratedFile != null && !temporaryGeneratedFile.delete()) {
+                log.warn("Could not delete generated output file {} from template {}", temporaryGeneratedFile, templateFile);
+            }
+        }
+    }
+
+    /**
+     * @param actionEvent {@link ActionEvent}
+     * @return true if plan is not dirty or has been saved 
+     */
+    boolean checkDirty(final ActionEvent actionEvent) {
+        ActionRouter.getInstance().doActionNow(new ActionEvent(actionEvent.getSource(), actionEvent.getID(), ActionNames.CHECK_DIRTY));
+        GuiPackage guiPackage = GuiPackage.getInstance();
+        if (guiPackage.isDirty()) {
+            // Check if the user wants to create from template
+            int response = JOptionPane.showConfirmDialog(GuiPackage.getInstance().getMainFrame(),
+                    JMeterUtils.getResString("cancel_new_from_template"), // $NON-NLS-1$
+                    JMeterUtils.getResString("template_load?"),  // $NON-NLS-1$
+                    JOptionPane.YES_NO_CANCEL_OPTION,
+                    JOptionPane.QUESTION_MESSAGE);
+            if (response == JOptionPane.YES_OPTION) {
+                ActionRouter.getInstance().doActionNow(new ActionEvent(actionEvent.getSource(), actionEvent.getID(), ActionNames.SAVE));
+                return true;
+            }
+            if (response == JOptionPane.CLOSED_OPTION || response == JOptionPane.CANCEL_OPTION) {
+                return false; // Don't clear the plan
+            }
+        }
+        return true;
+    }
+    
+    private Map<String, String> getUserParameters(){
+        Map<String, String> userParameters = new LinkedHashMap<>();
+        for (Entry<String, JLabeledTextField> entry : parametersTextFields.entrySet()) {
+            userParameters.put(entry.getKey(), entry.getValue().getText());
+        }
+        return userParameters;
     }
 
     private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
-        templateList.setValues(TemplateManager.getInstance().getTemplateNames());            
+        templateList.setValues(TemplateManager.getInstance().getTemplateNames());
         templateList.addChangeListener(this);
         reloadTemplateButton.addActionListener(this);
         reloadTemplateButton.setFont(FONT_SMALL);
-        this.getContentPane().setLayout(new BorderLayout(10, 0));
-        
-        JPanel templateBar = new JPanel(new BorderLayout());
-        templateBar.add(templateList, BorderLayout.CENTER);
-        JPanel reloadBtnBar = new JPanel();
-        reloadBtnBar.add(reloadTemplateButton);
-        templateBar.add(reloadBtnBar, BorderLayout.EAST);
-        this.getContentPane().add(templateBar, BorderLayout.NORTH);
         helpDoc.setContentType("text/html"); //$NON-NLS-1$
         helpDoc.setEditable(false);
         helpDoc.addHyperlinkListener(this);
-        this.getContentPane().add(scroller, BorderLayout.CENTER);
-
         applyTemplateButton.addActionListener(this);
         cancelButton.addActionListener(this);
-
-        // Bottom buttons bar
-        JPanel actionBtnBar = new JPanel(new FlowLayout());
-        actionBtnBar.add(applyTemplateButton);
-        actionBtnBar.add(cancelButton);
-        this.getContentPane().add(actionBtnBar, BorderLayout.SOUTH);
+        previous.addActionListener(this);
+        validateButton.addActionListener(this);
+        
+        // allow to reset the JDialog if the user click on the close button while
+        // it was displaying templates parameters
+        this.addWindowListener(new WindowAdapter(){
+            @Override
+            public void windowClosing(WindowEvent evt){
+                resetJDialog();
+                dispose();
+            }
+        });
+        this.setContentPane(templateSelectionPanel());
 
         this.pack();
         this.setMinimumSize(new Dimension(MINIMAL_BOX_WIDTH, MINIMAL_BOX_HEIGHT));
         ComponentUtil.centerComponentInWindow(this, 50); // center position and 50% of screen size
         populateTemplatePage();
     }
+    
+    private JPanel templateSelectionPanel() {
+        JPanel panel = new JPanel(new BorderLayout());
+        
+        JScrollPane scroller = new JScrollPane();
+        scroller.setViewportView(helpDoc);
+        JPanel templateBar = new JPanel(new BorderLayout());
+        templateBar.add(templateList, BorderLayout.CENTER);
+        JPanel reloadBtnBar = new JPanel();
+        reloadBtnBar.add(reloadTemplateButton);
+        templateBar.add(reloadBtnBar, BorderLayout.EAST);
+
+        // Bottom buttons bar
+        actionBtnBar.add(applyTemplateButton);
+        actionBtnBar.add(cancelButton);
+        
+        panel.add(templateBar, BorderLayout.NORTH);
+        panel.add(scroller, BorderLayout.CENTER);
+        panel.add(actionBtnBar, BorderLayout.SOUTH);
+        
+        return panel;
+    }
 
     /**
      * Do search
@@ -207,19 +301,44 @@ public class SelectTemplatesDialog exten
     public void actionPerformed(ActionEvent e) {
         final Object source = e.getSource();
         if (source == cancelButton) {
-            this.setVisible(false);
-            return;
+            resetJDialog();
+            this.dispose();
         } else if (source == applyTemplateButton) {
-            checkDirtyAndLoad(e);            
-        } else if (source == reloadTemplateButton) {
-            templateList.setValues(TemplateManager.getInstance().reset().getTemplateNames());
+            String selectedTemplate = templateList.getText();
+            Template template = TemplateManager.getInstance().getTemplateByName(selectedTemplate);
+            if (hasParameters(template)) {
+                this.setContentPane(configureParametersPanel(template.getParameters()));
+                this.revalidate();
+            } else {
+                checkDirtyAndLoad(e);
+            }
+        } else if (source == reloadTemplateButton || source == previous) {
+            resetJDialog();
+        } else if (source == validateButton) {
+            checkDirtyAndLoad(e);
+            resetJDialog();
         }
     }
     
+    /**
+     * 
+     * @param template {@link Template}
+     * @return true if template has not parameter 
+     */
+    private boolean hasParameters(Template template) {
+        return !(template.getParameters() == null || template.getParameters().isEmpty());
+    }
+    
     @Override
     public void stateChanged(ChangeEvent event) {
         populateTemplatePage();
     }
+    
+    private void resetJDialog() {
+        templateList.setValues(TemplateManager.getInstance().reset().getTemplateNames()); // reload templates
+        this.setContentPane(templateSelectionPanel());
+        this.revalidate();
+    }
 
     private void populateTemplatePage() {
         String selectedTemplate = templateList.getText();
@@ -230,6 +349,65 @@ public class SelectTemplatesDialog exten
                 : JMeterUtils.getResString("template_merge_from") );
     }
 
+    /**
+     * @param parameters {@link Map} parameters map
+     * @return JPanel from parameters
+     */
+    private JPanel configureParametersPanel(Map<String, String> parameters) {
+        JPanel panel = new JPanel(new BorderLayout());
+        
+        JPanel northPanel = new JPanel(new FlowLayout()); 
+        JLabel label = new JLabel(JMeterUtils.getResString("template_fill_parameters"));
+        label.setPreferredSize(new Dimension(150,35));
+        northPanel.add(label);
+        panel.add(northPanel, BorderLayout.NORTH);
+        
+        parametersTextFields.clear();
+        
+        GridBagConstraints gbc = new GridBagConstraints();
+        initConstraints(gbc);
+        int parameterCount = 0;
+        
+        JPanel gridbagpanel = new JPanel(new GridBagLayout());
+        for (Entry<String, String> entry : parameters.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            JLabeledTextField paramLabel = new JLabeledTextField(key + " : ");
+            paramLabel.setText(value);
+            parametersTextFields.put(key, paramLabel);
+
+            gbc.gridy = parameterCount++;
+            List<JComponent> listedParamLabel = paramLabel.getComponentList();
+            gridbagpanel.add(listedParamLabel.get(0), gbc.clone());
+            gbc.gridx = 1;
+            gridbagpanel.add(listedParamLabel.get(1), gbc.clone());
+            gbc.gridx = 0;
+        }
+        
+        JPanel actionBtnBarParameterPanel = new JPanel(new FlowLayout());
+        actionBtnBarParameterPanel.add(validateButton);
+        actionBtnBarParameterPanel.add(cancelButton);
+        actionBtnBarParameterPanel.add(previous);
+        
+        JScrollPane scroller = new JScrollPane(gridbagpanel);
+        panel.add(scroller, BorderLayout.CENTER);
+        panel.add(actionBtnBarParameterPanel, BorderLayout.SOUTH);
+        
+        return panel;
+    }
+    
+    private void initConstraints(GridBagConstraints gbc) {
+        gbc.anchor = GridBagConstraints.WEST;
+        gbc.insets = new Insets(0,0,5,0);
+        gbc.fill = GridBagConstraints.NONE;
+        gbc.gridheight = 1;
+        gbc.gridwidth = 1;
+        gbc.gridx = 0;
+        gbc.gridy = 0;
+        gbc.weightx = 0;
+        gbc.weighty = 0;
+    }
+
     @Override
     public void hyperlinkUpdate(HyperlinkEvent e) {
         if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED && java.awt.Desktop.isDesktopSupported()) {
@@ -240,5 +418,4 @@ public class SelectTemplatesDialog exten
             } 
         }
     }
-
 }

Modified: jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/Template.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/Template.java?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/Template.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/Template.java Tue Nov 27 20:23:44 2018
@@ -19,10 +19,12 @@
 package org.apache.jmeter.gui.action.template;
 
 import java.io.File;
+import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * Template Bean
- * @since 2.10
+ * @since 2.10 
  */
 public class Template {
     private boolean isTestPlan;
@@ -30,6 +32,7 @@ public class Template {
     private String fileName;
     private String description;
     private transient File parent; // for relative links
+    private Map<String, String> parameters;
     /**
      * @return the name
      */
@@ -78,4 +81,116 @@ public class Template {
     public void setParent(File parent) {
         this.parent = parent;
     }
+
+    public Map<String, String> getParameters() {
+        return parameters;
+    }
+    
+    public void setParameters(Map<String, String> parameters) {
+        this.parameters = parameters;
+    }
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((description == null) ? 0 : description.hashCode());
+        result = prime * result + ((fileName == null) ? 0 : fileName.hashCode());
+        result = prime * result + (isTestPlan ? 1231 : 1237);
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + ((parameters == null) ? 0 : parameters.hashCode());
+        result = prime * result + ((parent == null) ? 0 : parent.hashCode());
+        return result;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        Template other = (Template) obj;
+        if (description == null) {
+            if (other.description != null) {
+                return false;
+            }
+        } else if (!description.equals(other.description)) {
+            return false;
+        }
+        if (fileName == null) {
+            if (other.fileName != null) {
+                return false;
+            }
+        } else if (!fileName.equals(other.fileName)) {
+            return false;
+        }
+        if (isTestPlan != other.isTestPlan) {
+            return false;
+        }
+        if (name == null) {
+            if (other.name != null) {
+                return false;
+            }
+        } else if (!name.equals(other.name)) {
+            return false;
+        }
+        if (!mapsEquals(parameters, other.parameters)) {
+            return false;
+        }
+        if (parent == null) {
+            if (other.parent != null) {
+                return false;
+            }
+        } else if (!parent.equals(other.parent)) {
+            return false;
+        }
+        return true;
+    }
+    
+    private boolean mapsEquals(Map<String, String> map1, Map<String, String> map2) {
+        if(map1 == null) {
+            return map2 == null;
+        }else if(map2 == null) {
+            return false;
+        }
+        
+        if(map1.size() != map2.size()) {
+            return false;
+        }
+        
+        for(Entry<String, String> entry : map1.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            if(map2.containsKey(key)) {
+                if(!map2.get(key).equals(value)) {
+                    return false;
+                }
+            }else {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Template [isTestPlan=");
+        builder.append(isTestPlan);
+        builder.append(", name=");
+        builder.append(name);
+        builder.append(", fileName=");
+        builder.append(fileName);
+        builder.append(", description=");
+        builder.append(description);
+        builder.append(", parameters=");
+        builder.append(parameters);
+        builder.append("]");
+        return builder.toString();
+    }
 }

Modified: jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/TemplateManager.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/TemplateManager.java?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/TemplateManager.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/TemplateManager.java Tue Nov 27 20:23:44 2018
@@ -19,9 +19,12 @@
 package org.apache.jmeter.gui.action.template;
 
 import java.io.File;
-import java.util.LinkedHashMap;
+import java.io.IOException;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.TreeMap;
 
+import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
@@ -29,24 +32,21 @@ import org.apache.commons.lang3.StringUt
 import org.apache.jmeter.util.JMeterUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import com.thoughtworks.xstream.XStream;
-import com.thoughtworks.xstream.io.StreamException;
-import com.thoughtworks.xstream.io.xml.DomDriver;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
 
 /**
  * Manages Test Plan templates
  * @since 2.10
  */
 public class TemplateManager {
-    // Created by XStream reading templates.xml
-    private static class Templates {
-        /*
-         * N.B. Must use LinkedHashMap for field type
-         * XStream creates a plain HashMap if one uses Map as the field type.
-         */
-        private final LinkedHashMap<String, Template> templates = new LinkedHashMap<>();
-    }
     private static final String TEMPLATE_FILES = JMeterUtils.getPropDefault("template.files", // $NON-NLS-1$
             "/bin/templates/templates.xml");
 
@@ -56,52 +56,12 @@ public class TemplateManager {
     
     private final Map<String, Template> allTemplates;
 
-    private final XStream xstream = initXStream();
-
     public static TemplateManager getInstance() {
         return SINGLETON;
     }
     
     private TemplateManager()  {
-        allTemplates = readTemplates();            
-    }
-    
-    private XStream initXStream() {
-        XStream xstream = new XStream(new DomDriver(){
-            /**
-             * Create the DocumentBuilderFactory instance.
-             * See https://blog.compass-security.com/2012/08/secure-xml-parser-configuration/
-             * See https://github.com/x-stream/xstream/issues/25
-             * @return the new instance
-             */
-            @Override
-            protected DocumentBuilderFactory createDocumentBuilderFactory() {
-                final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-                try {
-                    factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
-                    factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
-                } catch (ParserConfigurationException e) {
-                    throw new StreamException(e);
-                }
-                factory.setExpandEntityReferences(false);
-                return factory;
-            }
-        });
-        JMeterUtils.setupXStreamSecurityPolicy(xstream);
-        xstream.alias("template", Template.class);
-        xstream.alias("templates", Templates.class);
-        xstream.useAttributeFor(Template.class, "isTestPlan");
-        
-        // templates i
-        xstream.addImplicitMap(Templates.class, 
-                // field TemplateManager#templates 
-                "templates", // $NON-NLS-1$
-                Template.class,     
-                // field Template#name 
-                "name" // $NON-NLS-1$
-                );
-                
-        return xstream;
+        allTemplates = readTemplates();
     }
 
     public void addTemplate(Template template) {
@@ -120,26 +80,26 @@ public class TemplateManager {
     }
 
     /**
-     * @return the templates names
+     * @return the templates names sorted in alphabetical order
      */
     public String[] getTemplateNames() {
         return allTemplates.keySet().toArray(new String[allTemplates.size()]);
     }
 
     private Map<String, Template> readTemplates() {
-        final Map<String, Template> temps = new LinkedHashMap<>();
+        final Map<String, Template> temps = new TreeMap<>();
        
         final String[] templateFiles = TEMPLATE_FILES.split(",");
         for (String templateFile : templateFiles) {
             if(!StringUtils.isEmpty(templateFile)) {
-                final File f = new File(JMeterUtils.getJMeterHome(), templateFile); 
+                final File file = new File(JMeterUtils.getJMeterHome(), templateFile); 
                 try {
-                    if(f.exists() && f.canRead()) {
+                    if(file.exists() && file.canRead()) {
                         if (log.isInfoEnabled()) {
-                            log.info("Reading templates from: {}", f.getAbsolutePath());
+                            log.info("Reading templates from: {}", file.getAbsolutePath());
                         }
-                        final File parent = f.getParentFile();
-                        final LinkedHashMap<String, Template> templates = ((Templates) xstream.fromXML(f)).templates;
+                        Map<String, Template> templates = parseTemplateFile(file);
+                        final File parent = file.getParentFile();
                         for(Template t : templates.values()) {
                             if (!t.getFileName().startsWith("/")) {
                                 t.setParent(parent);
@@ -149,12 +109,12 @@ public class TemplateManager {
                     } else {
                         if (log.isWarnEnabled()) {
                             log.warn("Ignoring template file:'{}' as it does not exist or is not readable",
-                                    f.getAbsolutePath());
+                                    file.getAbsolutePath());
                         }
                     }
                 } catch(Exception ex) {
                     if (log.isWarnEnabled()) {
-                        log.warn("Ignoring template file:'{}', an error occurred parsing the file", f.getAbsolutePath(),
+                        log.warn("Ignoring template file:'{}', an error occurred parsing the file", file.getAbsolutePath(),
                                 ex);
                     }
                 } 
@@ -162,6 +122,100 @@ public class TemplateManager {
         }
         return temps;
     }
+    
+    public final class LoggingErrorHandler implements ErrorHandler {
+        private Logger logger;
+        private File file;
+
+        public LoggingErrorHandler(Logger logger, File file) {
+            this.logger = logger;
+            this.file = file;
+        }
+        @Override
+        public void error(SAXParseException ex) throws SAXException {
+            throw ex;
+        }
+
+        @Override
+        public void fatalError(SAXParseException ex) throws SAXException {
+            throw ex;
+        }
+
+        @Override
+        public void warning(SAXParseException ex) throws SAXException {
+            logger.warn("Warning parsing file {}", file, ex);
+        }
+    }
+    
+    public static class DefaultEntityResolver implements EntityResolver {
+        public DefaultEntityResolver() {
+            super();
+        }
+
+        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+            if(systemId.endsWith("templates.dtd")) {
+                return new InputSource(TemplateManager.class.getResourceAsStream("/org/apache/jmeter/gui/action/template/templates.dtd"));
+            } else {
+                return null;
+            }
+        }
+    }
+
+    public Map<String, Template> parseTemplateFile(File file) throws IOException, SAXException, ParserConfigurationException{
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        dbf.setValidating(true);
+        dbf.setNamespaceAware(true);
+        dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
+        dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+        DocumentBuilder bd = dbf.newDocumentBuilder();
+        bd.setEntityResolver(new DefaultEntityResolver());
+        LoggingErrorHandler errorHandler = new LoggingErrorHandler(log, file);
+        bd.setErrorHandler(errorHandler);
+        Document document = bd.parse(file.getAbsolutePath());
+        document.getDocumentElement().normalize();
+        Map<String, Template> templates = new TreeMap<>();
+        NodeList templateNodes = document.getElementsByTagName("template");
+        for (int i = 0; i < templateNodes.getLength(); i++) {
+            Node node = templateNodes.item(i);
+            parseTemplateNode(templates, node);
+        }
+        return templates;
+    }
+
+    /**
+     * @param templates Map of {@link Template} referenced by name
+     * @param templateNode {@link Node} the xml template node
+     */
+    void parseTemplateNode(Map<String, Template> templates, Node templateNode) {
+        if (templateNode.getNodeType() == Node.ELEMENT_NODE) {
+            Template template = new Template();
+            Element element =  (Element) templateNode;
+            template.setTestPlan("true".equals(element.getAttribute("isTestPlan")));
+            template.setName(textOfFirstTag(element, "name"));
+            template.setDescription(textOfFirstTag(element, "description"));
+            template.setFileName(textOfFirstTag(element, "fileName"));
+            NodeList nl = element.getElementsByTagName("parameters");
+            if(nl.getLength()>0) {
+                NodeList parameterNodes = ((Element) nl.item(0)).getElementsByTagName("parameter");
+                Map<String, String> parameters = parseParameterNodes(parameterNodes);
+                template.setParameters(parameters);
+            }
+            templates.put(template.getName(), template);
+        }
+    }
+
+    private String textOfFirstTag(Element element, String tagName) {
+        return element.getElementsByTagName(tagName).item(0).getTextContent();
+    }
+    
+    private Map<String, String> parseParameterNodes(NodeList parameterNodes) {
+        Map<String, String> parametersMap = new HashMap<>();
+        for (int i = 0; i < parameterNodes.getLength(); i++) {
+            Element element =  (Element) parameterNodes.item(i);
+            parametersMap.put(element.getAttribute("key"), element.getAttribute("defaultValue"));
+        }
+        return parametersMap;
+    }
 
     /**
      * @param selectedTemplate Template name

Added: jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/templates.dtd
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/templates.dtd?rev=1847594&view=auto
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/templates.dtd (added)
+++ jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/templates.dtd Tue Nov 27 20:23:44 2018
@@ -0,0 +1,37 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<!--  Basic DTD for templates.xml -->
+<!ELEMENT templates (template+)>
+
+<!ELEMENT template (name, fileName, description, parameters?)>
+<!-- Whether the template is a complete test plan or not -->
+<!ATTLIST template isTestPlan (true|false) #REQUIRED>
+
+<!ELEMENT name  (#PCDATA)  >
+
+<!ELEMENT fileName  (#PCDATA)  >
+
+<!ELEMENT description  (#PCDATA)  >
+
+<!ELEMENT parameters (parameter*)>
+
+<!ELEMENT parameter EMPTY>
+
+<!ATTLIST parameter key CDATA #REQUIRED>
+
+<!ATTLIST parameter defaultValue CDATA #REQUIRED>

Propchange: jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/templates.dtd
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jmeter/trunk/src/core/org/apache/jmeter/gui/action/template/templates.dtd
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties Tue Nov 27 20:23:44 2018
@@ -1206,6 +1206,7 @@ teardown_on_shutdown=Run tearDown Thread
 template_choose=Select Template
 template_create_from=Create
 template_field=Template ($i$ where i is capturing group number, starts at 1):
+template_fill_parameters=Fill your parameters\:
 template_load?=Load template?
 template_menu=Templates...
 template_merge_from=Merge

Modified: jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties Tue Nov 27 20:23:44 2018
@@ -1195,6 +1195,7 @@ teardown_on_shutdown=Ex\u00E9cuter le Gr
 template_choose=Choisir le mod\u00E8le
 template_create_from=Cr\u00E9er
 template_field=Canevas \:
+template_fill_parameters=Remplir les param\u00E8tres \:
 template_load?=Charger le mod\u00E8le ?
 template_menu=Mod\u00E8les...
 template_merge_from=Fusionner

Added: jmeter/trunk/src/core/org/apache/jmeter/util/TemplateUtil.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/util/TemplateUtil.java?rev=1847594&view=auto
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/util/TemplateUtil.java (added)
+++ jmeter/trunk/src/core/org/apache/jmeter/util/TemplateUtil.java Tue Nov 27 20:23:44 2018
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.util;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+import freemarker.template.Configuration;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateExceptionHandler;
+
+/**
+ * Class used to process freemarkers templates
+ * @since 5.1
+ */
+public final class TemplateUtil {
+    
+    private static Configuration templateConfiguration = init();
+    
+    private TemplateUtil() {
+        super();
+    }
+    
+    private static Configuration init() {
+        Configuration templateConfiguration = new Configuration(Configuration.getVersion());
+        templateConfiguration.setDefaultEncoding(StandardCharsets.UTF_8.name());
+        templateConfiguration.setInterpolationSyntax(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX);
+        templateConfiguration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
+        return templateConfiguration;
+    }
+
+    /**
+     * Give a basic templateConfiguration
+     * @return a Configuration
+     */
+    public static Configuration getTemplateConfig() {
+        return templateConfiguration;
+    }
+    
+    /**
+     * Process a given freemarker template and put its result in a new folder.
+     * 
+     * @param template file that contains the freemarker template to process
+     * @param outputFile {@link File} created from template
+     * @param templateConfig Configuration of the template
+     * @param data to inject in the template
+     * @throws IOException if an I/O exception occurs during writing to the writer
+     * @throws TemplateException if an exception occurs during template processing
+     */
+    public static void processTemplate(File template, 
+            File outputFile,
+            Configuration templateConfig, Map<String, String> data) 
+                    throws IOException, TemplateException {
+        
+        templateConfig.setDirectoryForTemplateLoading(template.getParentFile());
+        freemarker.template.Template temp = templateConfig.getTemplate(template.getName());
+        try (FileOutputStream stream = new FileOutputStream(outputFile);
+                Writer writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
+                BufferedWriter bufferedWriter = new BufferedWriter(writer)){
+            temp.process(data, bufferedWriter);
+        }
+    }
+}

Propchange: jmeter/trunk/src/core/org/apache/jmeter/util/TemplateUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jmeter/trunk/src/core/org/apache/jmeter/util/TemplateUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/invalidTemplates.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/invalidTemplates.xml?rev=1847594&view=auto
==============================================================================
--- jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/invalidTemplates.xml (added)
+++ jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/invalidTemplates.xml Tue Nov 27 20:23:44 2018
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!DOCTYPE templates SYSTEM "templates.dtd">
+<templates>
+    <template isTestPlan="true">
+        <name>Buggy</name>
+        <fileName>/bin/templates/testTemplate.jmx.fmkr</fileName>
+        <description><![CDATA[
+            <h1>Test</h1>
+            <h2>Overview</h2>
+            This is a sample BeanShell sampler which shows how to use some of its
+            features.
+            <br />
+            Please select a suitable location in the tree before merging.
+            <h2>Useful links</h2>
+            <ul>
+                <li>
+                    <a
+                        href="http://jmeter.apache.org/usermanual/component_reference.html#BeanShell_Sampler">
+                        http://jmeter.apache.org/usermanual/component_reference.html#BeanShell_Sampler
+                    </a>
+                </li>
+            </ul>
+        ]]></description>
+        <parameters>
+            <key>citrixPortalHost</key>
+            <defaultValue>https://foo.com</defaultValue>
+        </parameters>
+    </template>
+</templates>
\ No newline at end of file

Propchange: jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/invalidTemplates.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/invalidTemplates.xml
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/validTemplates.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/validTemplates.xml?rev=1847594&view=auto
==============================================================================
--- jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/validTemplates.xml (added)
+++ jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/validTemplates.xml Tue Nov 27 20:23:44 2018
@@ -0,0 +1,23 @@
+<!DOCTYPE templates SYSTEM "templates.dtd">
+<templates>
+    <template isTestPlan="false">
+        <name>testTemplateNotTestPlan</name>
+        <fileName>/bin/templates/testTemplateNotTestPlan.jmx</fileName>
+        <description>testTemplateNotTestPlan desc</description>
+    </template>
+    <template isTestPlan="true">
+        <name>testTemplate</name>
+        <fileName>/bin/templates/testTemplate.jmx</fileName>
+        <description>testTemplate desc</description>
+    </template>
+    <template isTestPlan="true">
+        <name>testTemplateWithParameters</name>
+        <fileName>/bin/templates/testTemplate.jmx.fmkr</fileName>
+        <description>Template with parameters</description>
+        <parameters>
+            <parameter key="testKey1" defaultValue="n 1" />
+            <parameter key="testKey2" defaultValue="n 2" />
+            <parameter key="testKey3" defaultValue="n 3" />
+        </parameters>
+    </template>
+</templates>
\ No newline at end of file

Propchange: jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/validTemplates.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jmeter/trunk/test/resources/org/apache/jmeter/gui/action/template/validTemplates.xml
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jmeter/trunk/test/src/org/apache/jmeter/gui/action/template/TestTemplateManager.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/test/src/org/apache/jmeter/gui/action/template/TestTemplateManager.java?rev=1847594&view=auto
==============================================================================
--- jmeter/trunk/test/src/org/apache/jmeter/gui/action/template/TestTemplateManager.java (added)
+++ jmeter/trunk/test/src/org/apache/jmeter/gui/action/template/TestTemplateManager.java Tue Nov 27 20:23:44 2018
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.gui.action.template;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.jmeter.junit.JMeterTestCase;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Test TemplateManager Class
+ */
+public class TestTemplateManager extends JMeterTestCase {
+
+    /**
+     * Test a valid templateFile.
+     */
+    @Test
+    public void testTemplateFile() throws IOException, SAXException, ParserConfigurationException {
+        File xmlTemplate = new File(this.getClass().getResource("validTemplates.xml").getFile());
+        TemplateManager templateManager = TemplateManager.getInstance();
+        Map<String, Template> templateMap = templateManager.parseTemplateFile(xmlTemplate);
+        assertEquals(3, templateMap.size());
+        Template testTemplate = templateMap.get("testTemplateWithParameters");
+        assertTrue(testTemplate.isTestPlan());
+        assertEquals("testTemplateWithParameters", testTemplate.getName());
+        assertEquals("/bin/templates/testTemplate.jmx.fmkr", testTemplate.getFileName());
+        assertEquals("Template with parameters", testTemplate.getDescription());
+        Map<String, String> testTemplateParameters = testTemplate.getParameters();
+        assertEquals("n 1", testTemplateParameters.get("testKey1"));
+        assertEquals("n 2", testTemplateParameters.get("testKey2"));
+        assertEquals("n 3", testTemplateParameters.get("testKey3"));
+        
+        testTemplate = templateMap.get("testTemplateNotTestPlan");
+        assertFalse(testTemplate.isTestPlan());
+        assertEquals("testTemplateNotTestPlan", testTemplate.getName());
+        assertEquals("/bin/templates/testTemplateNotTestPlan.jmx", testTemplate.getFileName());
+        assertEquals("testTemplateNotTestPlan desc", testTemplate.getDescription());
+        assertNull(testTemplate.getParameters());
+        
+        testTemplate = templateMap.get("testTemplate");
+        assertTrue(testTemplate.isTestPlan());
+        assertEquals("testTemplate", testTemplate.getName());
+        assertEquals("/bin/templates/testTemplate.jmx", testTemplate.getFileName());
+        assertEquals("testTemplate desc", testTemplate.getDescription());
+        assertNull(testTemplate.getParameters());
+        
+    }
+
+    /**
+     * Check that a wrong xml file throws a FileNotFoundException
+     */
+    @Test(expected = FileNotFoundException.class)
+    public void testInvalidTemplateFile() throws Exception {
+        String xmlTemplatePath = "missing.xml";
+        File templateFile = new File(xmlTemplatePath);
+        TemplateManager templateManager = TemplateManager.getInstance();
+        templateManager.parseTemplateFile(templateFile);
+    }
+
+    @Test
+    public void testInvalidTemplateXml() throws IOException, SAXException, ParserConfigurationException {
+        try {
+            String xmlTemplatePath = this.getClass().getResource("invalidTemplates.xml").getFile();
+            File templateFile = new File(xmlTemplatePath);
+            TemplateManager templateManager = TemplateManager.getInstance();
+            templateManager.parseTemplateFile(templateFile);
+        } catch (SAXParseException ex) {
+            assertTrue("Exception did not contains expected message, got:"+ex.getMessage(), 
+                    ex.getMessage().indexOf("Element type \"key\" must be declared.")>=0);
+        }
+    }
+}

Propchange: jmeter/trunk/test/src/org/apache/jmeter/gui/action/template/TestTemplateManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jmeter/trunk/test/src/org/apache/jmeter/gui/action/template/TestTemplateManager.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1847594&r1=1847593&r2=1847594&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml [utf-8] (original)
+++ jmeter/trunk/xdocs/changes.xml [utf-8] Tue Nov 27 20:23:44 2018
@@ -125,6 +125,7 @@ of previous time slot as a base. Startin
    <li><pr>411</pr>Use <code>SHA-1</code> instead of <code>SHA1</code> in <code>org.apache.jmeter.save.SaveService</code>. Contributed by Paco (paco.xu at daocloud.io)</li>
    <li><bug>62914</bug>Add a hint in Thread Group UI about duration of test</li>
    <li><bug>62925</bug>Add support for ThreadDump to the JMeter non-GUI</li>
+   <li><bug>62870</bug>Templates : Add ability to provide parameters. Contributed by Ubik Load Pack (support at ubikloadpack.com)</li>
    <li><bug>62829</bug>Allow specifying Proxy server scheme for HTTP request sampler, Advanced tab and command line option. Contributed by Hitesh Patel (hitesh.h.patel at gmail.com)</li>
 </ul>
 

Added: jmeter/trunk/xdocs/creating-templates.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/creating-templates.xml?rev=1847594&view=auto
==============================================================================
--- jmeter/trunk/xdocs/creating-templates.xml (added)
+++ jmeter/trunk/xdocs/creating-templates.xml Tue Nov 27 20:23:44 2018
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+    license agreements. See the NOTICE file distributed with this work for additional 
+    information regarding copyright ownership. The ASF licenses this file to 
+    You under the Apache License, Version 2.0 (the "License"); you may not use 
+    this file except in compliance with the License. You may obtain a copy of 
+    the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required 
+    by applicable law or agreed to in writing, software distributed under the 
+    License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
+    OF ANY KIND, either express or implied. See the License for the specific 
+    language governing permissions and limitations under the License. -->
+
+<!DOCTYPE document[
+<!ENTITY hellip   "&#x02026;" >
+]>
+
+<document id="$Id$">
+    <properties>
+        <title>User guide: Customizables templates</title>
+    </properties>
+    <body>
+        <section name="Customizable template">
+            <p>
+                This document describes how to create a customizable template.
+            </p>
+            <subsection name="1 Folder structure" anchor="folder_structure">
+                    <p>
+                        The template feature uses the bin/templates folder which contains :<br/>
+                        <ul>
+                            <li>templates.xml, the file where you declare the templates you want to be able to use</li>
+                            <li>some .jmx and .jmx.fmkr files which are the templates</li>
+                        </ul>
+                    </p>
+                    <p>Here is how it looks like:
+                    <figure image="templates/template_folder.png">Figure 1 - template folder</figure>
+                    </p>
+            </subsection>
+            <subsection name="2 Template declaration" anchor="template_creation">
+                <subsection name="2.1 Basic template declaration" anchor="basic_template_declaration">
+                    <p>
+                        First of all you must declare your template. To do that, look into the templates.xml file.<br/>
+                        This file respect a DTD <br/>
+                        Below is the already existing Recording template declaration inside the templates.xml :
+                        <figure image="templates/templates_xml.png">Figure 2 - recording template declaration</figure>
+                        A template declaration is made as follow :<br/>
+                        <ul>
+                            <li><code>template</code> element which contains the informations described in the followings tags</li>
+                            <li><code>name</code> element which contains the template name the user will see</li>
+                            <li><code>fileName</code> element which contains the relative path of the template.</li>
+                            <li><code>description</code> element which uses html to describe the template</li>
+                            <li><code>optional</code> parameters tag (will be discussed later)</li>
+                        </ul>
+                    </p>
+                </subsection>
+                <subsection name="2.2 Customizable template declaration" anchor="customizable_template_declaration">
+                    <p>
+                        Let's say we want the exact same Recording template as in the 2.1 section, but we want to choose the name
+                        of the xml file where the recording of view result tree will be saved.<br/><br/>
+                        To do so we will use the parameters tag to tell JMeter to ask the user about a name for the concerned file :
+                        <figure image="templates/templates_xml_parameters.png">Figure 3 - recording template with parameters</figure>
+                        <note>You can put as many parameter tags as you want in the parameters tag.</note>
+                        Let's see what changed here.<br/>
+                        Firstly, customs templates are <code>.jmx.fmkr</code> files and not only <code>.jmx</code>.<br/><br/>
+                        Lastly, we added a <code>parameters</code> tag.<br/> As you can see in the image, a <code>parameters</code> tag contains <code>parameter</code> tags.<br/>
+                        Parameter tags are empty and contains 2 attributes :
+                        <ul>
+                            <li><code>key</code> is the name of the parameter you will ask the user to fill.</li>
+                            <li><code>defaultValue</code> is as its name says, the default value the user will see for the parameter.</li>
+                        </ul>
+                    </p>
+                </subsection>
+            </subsection>
+            <subsection name="3 Template file" anchor="template_file">
+                <subsection name="3.1 Basic template file" anchor="basic_template_file">
+                    <p>
+                        The template file is the one you used in the fileName tag when you declared your template.
+                        A template file is just the saving of a JMeter test plan.
+                    </p>
+                </subsection>
+                
+                <subsection name="3.2 Customizable template file" anchor="customizable_template_file">
+                    <p>
+                        In the 2.2 section we saw that a custom template file is a .jmx.fmkr file.<br/>
+                        The single difference between them is the .jmx.fmkr will be analyzed by JMeter to<br/>
+                        detect customs tag. If a custom tag is found, JMeter will try to replace it by the corresponding
+                        given value from the user.<br/><br/>
+                        A custom tag is defined as follow : 
+                        <source><![CDATA[[=<key>]]]></source>
+                        This is based on <a href="https://freemarker.apache.org/docs/dgui_misc_alternativesyntax.html#dgui_misc_alternativesyntax_interpolation">Freemarker alternative Interpolation syntax</a>.
+                        Let's illustrate how it works with an example.<br/>
+                        Consider the following part of the recording.jmx template file : <br/>
+                        <figure image="templates/template_recording_filename.png">Figure 4 - recording.jmx save file</figure>
+                        The surrounded area correspond to the name of the xml file where the View Results Tree output will be saved.
+                        As it is, when you use the template you will always have the same saving filename : <code>recording.xml</code>.<br/><br/>
+                        To make it customizable, change your recording template declaration in the templates.xml files by the<br/>
+                        one shown in the 2.2 section. Then, rename the recording.jmx file to recording.jmx.fmkr.<br/><br/>
+                        When it's done, Change the above selected line by this one : 
+                        <source><![CDATA[<stringProp name="filename">[=xmlFileName]</stringProp>]]></source>
+                        <br/>
+                        It's over ! With this configuration, if you chose to use the recording template, JMeter will ask you
+                        a xmlFileName (correspond to the key value in the declaration).
+                        <figure image="templates/template_parameters_window.png">Figure 5 - JMeter asks you the value you want to put for the key</figure>
+                        Then, you will find the expected value in the created template where you placed the [=xmlFileName] tag :
+                        <figure image="templates/template_recording_retrieved_value.png">Figure 6 - the value changed</figure>
+                    </p>
+                </subsection>
+            </subsection>
+        </section>
+    </body>
+</document>

Propchange: jmeter/trunk/xdocs/creating-templates.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jmeter/trunk/xdocs/creating-templates.xml
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jmeter/trunk/xdocs/images/screenshots/templates/template_folder.png
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/images/screenshots/templates/template_folder.png?rev=1847594&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jmeter/trunk/xdocs/images/screenshots/templates/template_folder.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jmeter/trunk/xdocs/images/screenshots/templates/template_parameters.png
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/images/screenshots/templates/template_parameters.png?rev=1847594&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jmeter/trunk/xdocs/images/screenshots/templates/template_parameters.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jmeter/trunk/xdocs/images/screenshots/templates/template_parameters_window.png
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/images/screenshots/templates/template_parameters_window.png?rev=1847594&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jmeter/trunk/xdocs/images/screenshots/templates/template_parameters_window.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jmeter/trunk/xdocs/images/screenshots/templates/template_recording_custom_filename.png
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/images/screenshots/templates/template_recording_custom_filename.png?rev=1847594&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jmeter/trunk/xdocs/images/screenshots/templates/template_recording_custom_filename.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jmeter/trunk/xdocs/images/screenshots/templates/template_recording_filename.png
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/images/screenshots/templates/template_recording_filename.png?rev=1847594&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jmeter/trunk/xdocs/images/screenshots/templates/template_recording_filename.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jmeter/trunk/xdocs/images/screenshots/templates/template_recording_retrieved_value.png
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/images/screenshots/templates/template_recording_retrieved_value.png?rev=1847594&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jmeter/trunk/xdocs/images/screenshots/templates/template_recording_retrieved_value.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: jmeter/trunk/xdocs/images/screenshots/templates/templates_xml_parameters.png
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/images/screenshots/templates/templates_xml_parameters.png?rev=1847594&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jmeter/trunk/xdocs/images/screenshots/templates/templates_xml_parameters.png
------------------------------------------------------------------------------
    svn:mime-type = image/png



Mime
View raw message