fix: add property bindings to reduce code duplication in UI classes and make "configure and modifyTestElement" simple in most cases
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/AbstractJMeterGuiComponent.java b/src/core/src/main/java/org/apache/jmeter/gui/AbstractJMeterGuiComponent.java
index f9920f1..4c6a791 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/AbstractJMeterGuiComponent.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/AbstractJMeterGuiComponent.java
@@ -35,9 +35,10 @@
 import javax.swing.JTextField;
 import javax.swing.border.Border;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.jmeter.gui.util.VerticalPanel;
 import org.apache.jmeter.testelement.TestElement;
-import org.apache.jmeter.testelement.property.StringProperty;
+import org.apache.jmeter.testelement.TestElementSchema;
 import org.apache.jmeter.util.JMeterUtils;
 import org.apache.jmeter.visualizers.Printable;
 import org.apache.jorphan.gui.JFactory;
@@ -84,6 +85,13 @@
     private final JTextArea commentField = JFactory.tabMovesFocus(new JTextArea());
 
     /**
+     * Stores a collection of property editors, so GuiCompoenent can have default implementations that
+     * update the UI fields based on {@link TestElement} properties and vice versa.
+     */
+    @API(status = EXPERIMENTAL, since = "5.6.3")
+    protected final BindingGroup bindingGroup = new BindingGroup();
+
+    /**
      * When constructing a new component, this takes care of basic tasks like
      * setting up the Name Panel and assigning the class's static label as the
      * name to start.
@@ -205,6 +213,7 @@
         setName(element.getName());
         enabled = element.isEnabled();
         commentField.setText(element.getComment());
+        bindingGroup.updateUi(element);
     }
 
     /**
@@ -228,6 +237,14 @@
         initGui();
     }
 
+    @Override
+    @API(status = EXPERIMENTAL, since = "5.6.3")
+    public void modifyTestElement(TestElement element) {
+        JMeterGUIComponent.super.modifyTestElement(element);
+        modifyTestElementEnabledAndComment(element);
+        bindingGroup.updateElement(element);
+    }
+
     /**
      * This provides a convenience for extenders when they implement the
      * {@link JMeterGUIComponent#modifyTestElement(TestElement)} method. This
@@ -235,21 +252,37 @@
      * Element. It should be called by every extending class when
      * creating/modifying Test Elements, as that will best assure consistent
      * behavior.
+     * <p>Deprecation notice: most likely you do not need the method, and you should
+     * override {@link #modifyTestElement(TestElement)} instead</p>
      *
      * @param mc
      *            the TestElement being created.
      */
+    @API(status = DEPRECATED, since = "5.6.3")
     protected void configureTestElement(TestElement mc) {
-        mc.setName(getName());
+        mc.setName(StringUtils.defaultIfEmpty(getName(), null));
+        TestElementSchema schema = TestElementSchema.INSTANCE;
+        mc.set(schema.getGuiClass(), getClass());
+        mc.set(schema.getTestClass(), mc.getClass());
+        modifyTestElementEnabledAndComment(mc);
+    }
 
-        mc.setProperty(new StringProperty(TestElement.GUI_CLASS, this.getClass().getName()));
-
-        mc.setProperty(new StringProperty(TestElement.TEST_CLASS, mc.getClass().getName()));
-
+    /**
+     * Assigns basic fields from UI to the test element: name, comments, gui class, and the registered editors.
+     *
+     * @param mc test element
+     */
+    private void modifyTestElementEnabledAndComment(TestElement mc) {
         // This stores the state of the TestElement
         log.debug("setting element to enabled: {}", enabled);
-        mc.setEnabled(enabled);
-        mc.setComment(getComment());
+        // We can skip storing "enabled" state if it's true, as it's default value.
+        // JMeter removes disabled elements early from the tree, so configuration elements
+        // with enabled=false (~HTTP Request Defaults) can't unexpectedly override the regular ones
+        // like HTTP Request.
+        mc.set(TestElementSchema.INSTANCE.getEnabled(), enabled ? null : Boolean.FALSE);
+        // Note: we can't use editors for "comments" as getComments() is not a final method, so plugins might
+        // override it and provide a different implementation.
+        mc.setComment(StringUtils.defaultIfEmpty(getComment(), null));
     }
 
     /**
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/JMeterGUIComponent.java b/src/core/src/main/java/org/apache/jmeter/gui/JMeterGUIComponent.java
index 17b5676..c7df480 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/JMeterGUIComponent.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/JMeterGUIComponent.java
@@ -21,7 +21,10 @@
 
 import javax.swing.JPopupMenu;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.jmeter.testelement.TestElement;
+import org.apache.jmeter.testelement.TestElementSchema;
+import org.apiguardian.api.API;
 
 /**
  * Implementing this interface indicates that the class is a JMeter GUI
@@ -82,9 +85,9 @@
      * the component's label in the local language. The resource name is fixed,
      * and does not vary with the selected language.
      *
-     * Normally this method should be overridden in preference to overriding
+     * <p>Normally this method should be overridden in preference to overriding
      * getStaticLabel(). However where the resource name is not available or required,
-     * getStaticLabel() may be overridden instead.
+     * getStaticLabel() may be overridden instead.</p>
      *
      * @return the resource name
      */
@@ -105,8 +108,19 @@
      * the model class that it knows how to display, and this method is called
      * when new test elements are created.
      *
+     * <p>Since 5.6.3, the default implementation is as follows, and subclasses should override
+     * {@link #makeTestElement()}
+     * <pre>
+     * public TestElement createTestElement() {
+     *     TestElement element = makeTestElement();
+     *     assignDefaultValues(element);
+     *     return el;
+     * }
+     * </pre>
+     *
      * <p>
-     * The canonical implementation looks like this:
+     * Before 5.6.3 the canonical implementation was as follows, however, it is recommended to
+     * avoid overriding {@link #createTestElement()} and override {@link #makeTestElement()} instead.
      * <pre>
      * public TestElement createTestElement() {
      *     TestElementXYZ el = new TestElementXYZ();
@@ -117,7 +131,35 @@
      *
      * @return the Test Element object that the GUI component represents.
      */
-    TestElement createTestElement();
+    @API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+    default TestElement createTestElement() {
+        TestElement element = makeTestElement();
+        assignDefaultValues(element);
+        return element;
+    }
+
+    /**
+     * Creates the test element represented by the GUI component.
+     * @since 5.6.3
+     * @return a new {@link TestElement}
+     */
+    @API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+    default TestElement makeTestElement() {
+        throw new UnsupportedOperationException("Please override makeTestElement with creating the element you need: return new ....");
+    }
+
+    /**
+     * Configures default values for element after its creation.
+     * Plugin authors should call this once in their {@link #createTestElement()} implementation.
+     * @since 5.6.3
+     * @param element test element to configure
+     */
+    default void assignDefaultValues(TestElement element) {
+        element.setName(StringUtils.defaultIfEmpty(getStaticLabel(), null));
+        TestElementSchema schema = TestElementSchema.INSTANCE;
+        element.set(schema.getGuiClass(), getClass());
+        element.set(schema.getTestClass(), element.getClass());
+    }
 
     /**
      * GUI components are responsible for populating TestElements they create
@@ -127,19 +169,26 @@
      * information.
      *
      * <p>
+     * If you override {@link AbstractJMeterGuiComponent}, you might want using {@link AbstractJMeterGuiComponent#bindingGroup}
+     * instead of overriding {@code modifyTestElement}.
+     *
+     * <p>
      * The canonical implementation looks like this:
      * <pre>
+     * &#064;Override
      * public void modifyTestElement(TestElement element) {
-     *     element.clear(); // many implementations use this
-     *     configureTestElement(element);
+     *     super.modifyTestElement(element); // clear the element and assign basic fields like name, gui class, test class
      *     // Using the element setters (preferred):
+     *     // If the field is empty, you probably want to remove the property instead of storing an empty string
+     *     // See <a href="https://github.com/apache/jmeter/pull/6199">Streamline binding of UI elements to TestElement properties</a>
+     *     // for more details
      *     TestElementXYZ xyz = (TestElementXYZ) element;
-     *     xyz.setState(guiState.getText());
-     *     xyz.setCode(guiCode.getText());
+     *     xyz.setState(StringUtils.defaultIfEmpty(guiState.getText(), null));
+     *     xyz.setCode(StringUtils.defaultIfEmpty(guiCode.getText(), null));
      *     ... other GUI fields ...
      *     // or directly (do not use unless there is no setter for the field):
-     *     element.setProperty(TestElementXYZ.STATE, guiState.getText())
-     *     element.setProperty(TestElementXYZ.CODE, guiCode.getText())
+     *     element.setProperty(TestElementXYZ.STATE, StringUtils.defaultIfEmpty(guiState.getText(), null))
+     *     element.setProperty(TestElementXYZ.CODE, StringUtils.defaultIfEmpty(guiCode.getText(), null))
      *     ... other GUI fields ...
      * }
      * </pre>
@@ -147,7 +196,16 @@
      * @param element
      *            the TestElement to modify
      */
-    void modifyTestElement(TestElement element);
+    @API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+    default void modifyTestElement(TestElement element) {
+        // TODO: should we keep .clear() here? It probably makes it easier to remove all properties before populating
+        //   the values from UI, however, it might be inefficient.
+        element.clear();
+        element.setName(StringUtils.defaultIfEmpty(getName(), null));
+        TestElementSchema schema = TestElementSchema.INSTANCE;
+        element.set(schema.getGuiClass(), getClass());
+        element.set(schema.getTestClass(), element.getClass());
+    }
 
     /**
      * Test GUI elements can be disabled, in which case they do not become part
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/dsl/DslPrinterTraverser.kt b/src/core/src/main/kotlin/org/apache/jmeter/dsl/DslPrinterTraverser.kt
index a170222..d52faa3 100644
--- a/src/core/src/main/kotlin/org/apache/jmeter/dsl/DslPrinterTraverser.kt
+++ b/src/core/src/main/kotlin/org/apache/jmeter/dsl/DslPrinterTraverser.kt
@@ -115,11 +115,9 @@
                 if (prop == TestElementSchema.testClass && stringValue == te::class.java.name && canSkipTestClass) {
                     continue
                 }
-                if ((property is StringProperty && stringValue.isNullOrEmpty()) ||
-                    stringValue == prop?.defaultValueAsString
-                ) {
-                    continue
-                }
+                // It might be tempting to skip printing the property if its value matches the default value,
+                // However, it would be wrong because "unset" values might be overriden by "... Request Defaults",
+                // so we do not want accidental overrides if the user explicitly set some of the properties
             }
 
             if (prop == null) {
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/gui/Binding.kt b/src/core/src/main/kotlin/org/apache/jmeter/gui/Binding.kt
new file mode 100644
index 0000000..d3aca2e
--- /dev/null
+++ b/src/core/src/main/kotlin/org/apache/jmeter/gui/Binding.kt
@@ -0,0 +1,42 @@
+/*
+ * 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
+
+import org.apache.jmeter.testelement.TestElement
+import org.apache.jmeter.testelement.schema.PropertyDescriptor
+import org.apiguardian.api.API
+
+/**
+ * Binds a UI control to a [PropertyDescriptor], so JMeter can automatically update the test element
+ * from the UI state and vice versa.
+ * @since 5.6.3
+ */
+@API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+public interface Binding {
+    /**
+     * Update [TestElement] based on the state of the UI.
+     * @param testElement element to update
+     */
+    public fun updateElement(testElement: TestElement)
+
+    /**
+     * Update UI based on the state of the given [TestElement].
+     * @param testElement element to get the state from
+     */
+    public fun updateUi(testElement: TestElement)
+}
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/gui/BindingGroup.kt b/src/core/src/main/kotlin/org/apache/jmeter/gui/BindingGroup.kt
new file mode 100644
index 0000000..9af3ce5
--- /dev/null
+++ b/src/core/src/main/kotlin/org/apache/jmeter/gui/BindingGroup.kt
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+import org.apache.jmeter.testelement.TestElement
+import org.apiguardian.api.API
+
+/**
+ * Manages a collection of [Binding]s.
+ * It enables to update a [TestElement] from the UI and vice versa with a common implementation.
+ * @since 5.6.3
+ */
+@API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+public class BindingGroup() : Binding {
+    private val bindings = mutableListOf<Binding>()
+
+    public constructor(bindings: Collection<Binding>) : this() {
+        addAll(bindings)
+    }
+
+    public fun add(binding: Binding) {
+        bindings += binding
+    }
+
+    public fun addAll(bindings: Collection<Binding>) {
+        this.bindings.addAll(bindings)
+    }
+
+    override fun updateElement(testElement: TestElement) {
+        bindings.forEach { it.updateElement(testElement) }
+    }
+
+    override fun updateUi(testElement: TestElement) {
+        bindings.forEach { it.updateUi(testElement) }
+    }
+}
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/gui/FilePanelEntryBinding.kt b/src/core/src/main/kotlin/org/apache/jmeter/gui/FilePanelEntryBinding.kt
new file mode 100644
index 0000000..0741458
--- /dev/null
+++ b/src/core/src/main/kotlin/org/apache/jmeter/gui/FilePanelEntryBinding.kt
@@ -0,0 +1,47 @@
+/*
+ * 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
+
+import org.apache.jmeter.gui.util.FilePanelEntry
+import org.apache.jmeter.testelement.TestElement
+import org.apache.jmeter.testelement.schema.PropertyDescriptor
+import org.apiguardian.api.API
+
+/**
+ * Binds a [FilePanelEntry] to a [PropertyDescriptor], so JMeter can automatically update the test element
+ * from the UI state and vice versa.
+ * @since 5.6.3
+ */
+@API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+public class FilePanelEntryBinding(
+    private val filePanelEntry: FilePanelEntry,
+    private val propertyDescriptor: PropertyDescriptor<*, *>,
+) : Binding {
+    override fun updateElement(testElement: TestElement) {
+        testElement[propertyDescriptor] = filePanelEntry.filename.takeIf { it.isNotEmpty() }
+    }
+
+    override fun updateUi(testElement: TestElement) {
+        filePanelEntry.filename =
+            if (testElement.getPropertyOrNull(propertyDescriptor) == null) {
+                ""
+            } else {
+                testElement.getString(propertyDescriptor)
+            }
+    }
+}
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/gui/JBooleanPropertyEditor.kt b/src/core/src/main/kotlin/org/apache/jmeter/gui/JBooleanPropertyEditor.kt
index 3ff38b3..1b4e70a 100644
--- a/src/core/src/main/kotlin/org/apache/jmeter/gui/JBooleanPropertyEditor.kt
+++ b/src/core/src/main/kotlin/org/apache/jmeter/gui/JBooleanPropertyEditor.kt
@@ -32,7 +32,7 @@
 public class JBooleanPropertyEditor(
     private val propertyDescriptor: BooleanPropertyDescriptor<*>,
     label: String,
-) : JEditableCheckBox(label, DEFAULT_CONFIGURATION) {
+) : JEditableCheckBox(label, DEFAULT_CONFIGURATION), Binding {
     private companion object {
         @JvmField
         val DEFAULT_CONFIGURATION: Configuration = Configuration(
@@ -50,26 +50,18 @@
         value = Value.of(propertyDescriptor.defaultValue ?: false)
     }
 
-    /**
-     * Update [TestElement] based on the state of the UI.
-     * TODO: we might better use PropertiesAccessor<TestElement, ElementSchema>
-     *     However, it would require callers to pass element.getProps() which might allocate.
-     * @param testElement element to update
-     */
-    public fun updateElement(testElement: TestElement) {
+    public override fun updateElement(testElement: TestElement) {
         when (val value = value) {
-            is Value.Boolean -> testElement[propertyDescriptor] = value.value
+            // For now, UI does not distinguish between "false" and "absent" values,
+            // so we treat "false" as "absent".
+            is Value.Boolean ->
+                testElement[propertyDescriptor] =
+                    value.value.takeIf { it || propertyDescriptor.defaultValue == true }
             is Value.Text -> testElement[propertyDescriptor] = value.value
         }
     }
 
-    /**
-     * Update UI based on the state of the given [TestElement].
-     * TODO: we might better use PropertiesAccessor<TestElement, ElementSchema>
-     *     However, it would require callers to pass element.getProps() which might allocate.
-     * @param testElement element to get the state from
-     */
-    public fun updateUi(testElement: TestElement) {
+    public override fun updateUi(testElement: TestElement) {
         value = when (val value = testElement.getPropertyOrNull(propertyDescriptor)) {
             is BooleanProperty, null -> Value.of(value?.booleanValue ?: propertyDescriptor.defaultValue ?: false)
             // TODO: should we rather fail in case we detect an unknown property?
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/gui/JCheckBoxBinding.kt b/src/core/src/main/kotlin/org/apache/jmeter/gui/JCheckBoxBinding.kt
new file mode 100644
index 0000000..a5744f6
--- /dev/null
+++ b/src/core/src/main/kotlin/org/apache/jmeter/gui/JCheckBoxBinding.kt
@@ -0,0 +1,43 @@
+/*
+ * 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
+
+import org.apache.jmeter.testelement.TestElement
+import org.apache.jmeter.testelement.schema.BooleanPropertyDescriptor
+import org.apache.jmeter.testelement.schema.PropertyDescriptor
+import org.apiguardian.api.API
+import javax.swing.JCheckBox
+
+/**
+ * Binds a [JCheckBox] to a [PropertyDescriptor], so JMeter can automatically update the test element
+ * from the UI state and vice versa.
+ * @since 5.6.3
+ */
+@API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+public class JCheckBoxBinding(
+    private val checkbox: JCheckBox,
+    private val propertyDescriptor: BooleanPropertyDescriptor<*>,
+) : Binding {
+    override fun updateElement(testElement: TestElement) {
+        testElement[propertyDescriptor] = checkbox.isSelected.takeIf { it }
+    }
+
+    override fun updateUi(testElement: TestElement) {
+        checkbox.isSelected = testElement[propertyDescriptor]
+    }
+}
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/gui/JLabeledFieldBinding.kt b/src/core/src/main/kotlin/org/apache/jmeter/gui/JLabeledFieldBinding.kt
new file mode 100644
index 0000000..cdf5e83
--- /dev/null
+++ b/src/core/src/main/kotlin/org/apache/jmeter/gui/JLabeledFieldBinding.kt
@@ -0,0 +1,47 @@
+/*
+ * 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
+
+import org.apache.jmeter.testelement.TestElement
+import org.apache.jmeter.testelement.schema.PropertyDescriptor
+import org.apache.jorphan.gui.JLabeledField
+import org.apiguardian.api.API
+
+/**
+ * Binds a [JLabeledField] to a [PropertyDescriptor], so JMeter can automatically update the test element
+ * from the UI state and vice versa.
+ * @since 5.6.3
+ */
+@API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+public class JLabeledFieldBinding(
+    private val labeledField: JLabeledField,
+    private val propertyDescriptor: PropertyDescriptor<*, *>,
+) : Binding {
+    override fun updateElement(testElement: TestElement) {
+        testElement[propertyDescriptor] = labeledField.text.takeIf { it.isNotEmpty() }
+    }
+
+    override fun updateUi(testElement: TestElement) {
+        labeledField.text =
+            if (testElement.getPropertyOrNull(propertyDescriptor) == null) {
+                ""
+            } else {
+                testElement.getString(propertyDescriptor)
+            }
+    }
+}
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/gui/JSyntaxTextAreaBinding.kt b/src/core/src/main/kotlin/org/apache/jmeter/gui/JSyntaxTextAreaBinding.kt
new file mode 100644
index 0000000..5f6b970
--- /dev/null
+++ b/src/core/src/main/kotlin/org/apache/jmeter/gui/JSyntaxTextAreaBinding.kt
@@ -0,0 +1,49 @@
+/*
+ * 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
+
+import org.apache.jmeter.gui.util.JSyntaxTextArea
+import org.apache.jmeter.testelement.TestElement
+import org.apache.jmeter.testelement.schema.PropertyDescriptor
+import org.apiguardian.api.API
+
+/**
+ * Binds a [JSyntaxTextArea] to a [PropertyDescriptor], so JMeter can automatically update the test element
+ * from the UI state and vice versa.
+ * @since 5.6.3
+ */
+@API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+public class JSyntaxTextAreaBinding(
+    private val syntaxTextArea: JSyntaxTextArea,
+    private val propertyDescriptor: PropertyDescriptor<*, *>,
+) : Binding {
+    override fun updateElement(testElement: TestElement) {
+        testElement[propertyDescriptor] = syntaxTextArea.text.takeIf { it.isNotEmpty() }
+    }
+
+    override fun updateUi(testElement: TestElement) {
+        syntaxTextArea.setInitialText(
+            if (testElement.getPropertyOrNull(propertyDescriptor) == null) {
+                ""
+            } else {
+                testElement.getString(propertyDescriptor)
+            }
+        )
+        syntaxTextArea.caretPosition = 0
+    }
+}
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/gui/JTextComponentBinding.kt b/src/core/src/main/kotlin/org/apache/jmeter/gui/JTextComponentBinding.kt
new file mode 100644
index 0000000..69bd883
--- /dev/null
+++ b/src/core/src/main/kotlin/org/apache/jmeter/gui/JTextComponentBinding.kt
@@ -0,0 +1,52 @@
+/*
+ * 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
+
+import org.apache.jmeter.testelement.TestElement
+import org.apache.jmeter.testelement.schema.PropertyDescriptor
+import org.apiguardian.api.API
+import javax.swing.JPasswordField
+import javax.swing.text.JTextComponent
+
+/**
+ * Binds a [JTextComponent] to a [PropertyDescriptor], so JMeter can automatically update the test element
+ * from the UI state and vice versa.
+ * @since 5.6.3
+ */
+@API(status = API.Status.EXPERIMENTAL, since = "5.6.3")
+public class JTextComponentBinding(
+    private val textComponent: JTextComponent,
+    private val propertyDescriptor: PropertyDescriptor<*, *>,
+) : Binding {
+    override fun updateElement(testElement: TestElement) {
+        val text = when (val component = textComponent) {
+            is JPasswordField -> String(component.password)
+            else -> component.text
+        }
+        testElement[propertyDescriptor] = text.takeIf { it.isNotEmpty() }
+    }
+
+    override fun updateUi(testElement: TestElement) {
+        textComponent.text =
+            if (testElement.getPropertyOrNull(propertyDescriptor) == null) {
+                ""
+            } else {
+                testElement.getString(propertyDescriptor)
+            }
+    }
+}
diff --git a/src/jorphan/src/main/kotlin/org/apache/jorphan/gui/JEditableCheckBox.kt b/src/jorphan/src/main/kotlin/org/apache/jorphan/gui/JEditableCheckBox.kt
index 4ca3c5f..ea825cf 100644
--- a/src/jorphan/src/main/kotlin/org/apache/jorphan/gui/JEditableCheckBox.kt
+++ b/src/jorphan/src/main/kotlin/org/apache/jorphan/gui/JEditableCheckBox.kt
@@ -20,6 +20,8 @@
 import org.apiguardian.api.API
 import java.awt.Container
 import java.awt.FlowLayout
+import java.awt.event.ActionEvent
+import javax.swing.AbstractAction
 import javax.swing.Box
 import javax.swing.JCheckBox
 import javax.swing.JComboBox
@@ -41,6 +43,7 @@
     public companion object {
         public const val CHECKBOX_CARD: String = "checkbox"
         public const val EDITABLE_CARD: String = "editable"
+        public const val VALUE_PROPERTY: String = "value"
     }
 
     /**
@@ -92,14 +95,21 @@
 
     private val cards = CardLayoutWithSizeOfCurrentVisibleElement()
 
+    private val useExpressionAction = object : AbstractAction(configuration.startEditing) {
+        override fun actionPerformed(e: ActionEvent?) {
+            cards.next(this@JEditableCheckBox)
+            comboBox.requestFocusInWindow()
+            fireValueChanged()
+        }
+    }
+
     private val checkbox: JCheckBox = JCheckBox(label).apply {
+        val cb = this
         componentPopupMenu = JPopupMenu().apply {
-            add(configuration.startEditing).apply {
-                addActionListener {
-                    cards.next(this@JEditableCheckBox)
-                    comboBox.requestFocusInWindow()
-                }
-            }
+            add(useExpressionAction)
+        }
+        addItemListener {
+            fireValueChanged()
         }
     }
 
@@ -114,6 +124,7 @@
             val jComboBox = it.source as JComboBox<*>
             SwingUtilities.invokeLater {
                 if (jComboBox.isPopupVisible) {
+                    fireValueChanged()
                     return@invokeLater
                 }
                 when (val value = jComboBox.selectedItem as String) {
@@ -121,10 +132,12 @@
                         checkbox.isSelected = value == configuration.trueValue
                         cards.show(this@JEditableCheckBox, CHECKBOX_CARD)
                         checkbox.requestFocusInWindow()
+                        fireValueChanged()
                     }
                 }
             }
         }
+        // TODO: trigger value changed when the text is changed
     }
 
     private val textFieldLabel = JLabel(label).apply {
@@ -157,6 +170,23 @@
         )
     }
 
+    private var oldValue = value
+
+    override fun setEnabled(enabled: Boolean) {
+        super.setEnabled(enabled)
+        checkbox.isEnabled = enabled
+        comboBox.isEnabled = enabled
+        useExpressionAction.isEnabled = enabled
+    }
+
+    private fun fireValueChanged() {
+        val newValue = value
+        if (value != oldValue) {
+            firePropertyChange(VALUE_PROPERTY, oldValue, newValue)
+            oldValue = newValue
+        }
+    }
+
     public var value: Value
         get() = when (components.indexOfFirst { it.isVisible }) {
             0 -> if (checkbox.isSelected) Value.Boolean.TRUE else Value.Boolean.FALSE
@@ -176,6 +206,7 @@
                     cards.show(this, EDITABLE_CARD)
                 }
             }
+            fireValueChanged()
         }
 
     @get:JvmSynthetic
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/GraphQLUrlConfigGui.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/GraphQLUrlConfigGui.java
index 9f461e6..d729b0e 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/GraphQLUrlConfigGui.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/GraphQLUrlConfigGui.java
@@ -58,12 +58,6 @@
     private static final UrlConfigDefaults URL_CONFIG_DEFAULTS = new UrlConfigDefaults();
     static {
         URL_CONFIG_DEFAULTS.setValidMethods(new String[] { HTTPConstants.POST, HTTPConstants.GET });
-        URL_CONFIG_DEFAULTS.setDefaultMethod(HTTPConstants.POST);
-        URL_CONFIG_DEFAULTS.setAutoRedirects(false);
-        URL_CONFIG_DEFAULTS.setFollowRedirects(false);
-        URL_CONFIG_DEFAULTS.setUseBrowserCompatibleMultipartMode(false);
-        URL_CONFIG_DEFAULTS.setUseKeepAlive(true);
-        URL_CONFIG_DEFAULTS.setUseMultipart(false);
         URL_CONFIG_DEFAULTS.setUseMultipartVisible(false);
     }
 
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java
index 8d8dd64..61f5b56 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java
@@ -19,10 +19,9 @@
 
 import java.awt.BorderLayout;
 import java.awt.Dimension;
-import java.awt.event.ItemEvent;
+import java.util.Arrays;
 
 import javax.swing.BorderFactory;
-import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
@@ -34,6 +33,8 @@
 import org.apache.jmeter.config.ConfigTestElement;
 import org.apache.jmeter.config.gui.AbstractConfigGui;
 import org.apache.jmeter.gui.GUIMenuSortOrder;
+import org.apache.jmeter.gui.JBooleanPropertyEditor;
+import org.apache.jmeter.gui.JTextComponentBinding;
 import org.apache.jmeter.gui.TestElementMetadata;
 import org.apache.jmeter.gui.util.HorizontalPanel;
 import org.apache.jmeter.gui.util.VerticalPanel;
@@ -43,6 +44,7 @@
 import org.apache.jmeter.testelement.AbstractTestElement;
 import org.apache.jmeter.testelement.TestElement;
 import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jorphan.gui.JEditableCheckBox;
 import org.apache.jorphan.gui.JFactory;
 
 import net.miginfocom.swing.MigLayout;
@@ -57,10 +59,16 @@
     private static final long serialVersionUID = 242L;
 
     private UrlConfigGui urlConfigGui;
-    private JCheckBox retrieveEmbeddedResources;
-    private JCheckBox concurrentDwn;
+    private final JBooleanPropertyEditor retrieveEmbeddedResources = new JBooleanPropertyEditor(
+            HTTPSamplerBaseSchema.INSTANCE.getRetrieveEmbeddedResources(),
+            JMeterUtils.getResString("web_testing_retrieve_images"));
+    private final JBooleanPropertyEditor concurrentDwn = new JBooleanPropertyEditor(
+            HTTPSamplerBaseSchema.INSTANCE.getConcurrentDownload(),
+            JMeterUtils.getResString("web_testing_concurrent_download"));
     private JTextField concurrentPool;
-    private JCheckBox useMD5;
+    private final JBooleanPropertyEditor useMD5 = new JBooleanPropertyEditor(
+            HTTPSamplerBaseSchema.INSTANCE.getStoreAsMD5(),
+            JMeterUtils.getResString("response_save_as_md5")); // $NON-NLS-1$
     private JTextField embeddedAllowRE; // regular expression used to match against embedded resource URLs to allow
     private JTextField embeddedExcludeRE; // regular expression used to match against embedded resource URLs to discard
     private JTextField sourceIpAddr; // does not apply to Java implementation
@@ -77,6 +85,27 @@
     public HttpDefaultsGui() {
         super();
         init();
+        HTTPSamplerBaseSchema schema = HTTPSamplerBaseSchema.INSTANCE;
+        bindingGroup.addAll(
+                Arrays.asList(
+                        retrieveEmbeddedResources,
+                        concurrentDwn,
+                        new JTextComponentBinding(concurrentPool, schema.getConcurrentDownloadPoolSize()),
+                        useMD5,
+                        new JTextComponentBinding(embeddedAllowRE, schema.getEmbeddedUrlAllowRegex()),
+                        new JTextComponentBinding(embeddedExcludeRE, schema.getEmbeddedUrlExcludeRegex()),
+                        new JTextComponentBinding(sourceIpAddr, schema.getIpSource()),
+                        // TODO: sourceIpType
+                        new JTextComponentBinding(proxyScheme, schema.getProxy().getScheme()),
+                        new JTextComponentBinding(proxyHost, schema.getProxy().getHost()),
+                        new JTextComponentBinding(proxyPort, schema.getProxy().getPort()),
+                        new JTextComponentBinding(proxyUser, schema.getProxy().getUsername()),
+                        new JTextComponentBinding(proxyPass, schema.getProxy().getPassword()),
+                        // TODO: httpImplementation
+                        new JTextComponentBinding(connectTimeOut, schema.getConnectTimeout()),
+                        new JTextComponentBinding(responseTimeOut, schema.getResponseTimeout())
+                )
+        );
     }
 
     @Override
@@ -95,80 +124,39 @@
     }
 
     /**
-     * Treat unset checkbox as empty, so "unset" checkboxes do not override values in HTTP Request Samplers.
-     */
-    private static Boolean nullIfUnset(JCheckBox checkBox) {
-        return checkBox.isSelected() ? true : null;
-    }
-
-    /**
      * Modifies a given TestElement to mirror the data in the gui components.
      *
      * @see org.apache.jmeter.gui.JMeterGUIComponent#modifyTestElement(TestElement)
      */
     @Override
     public void modifyTestElement(TestElement config) {
-        ConfigTestElement cfg = (ConfigTestElement) config;
-        ConfigTestElement el = (ConfigTestElement) urlConfigGui.createTestElement();
-        cfg.clear();
-        cfg.addConfigElement(el);
-        super.configureTestElement(config);
-        HTTPSamplerBaseSchema.INSTANCE httpSchema = HTTPSamplerBaseSchema.INSTANCE;
-        config.set(httpSchema.getRetrieveEmbeddedResources(), nullIfUnset(retrieveEmbeddedResources));
-        enableConcurrentDwn(retrieveEmbeddedResources.isSelected());
-        if (concurrentDwn.isSelected()) {
-            config.set(httpSchema.getConcurrentDownload(), true);
-            config.set(httpSchema.getConcurrentDownloadPoolSize(), concurrentPool.getText());
-        } else {
-            config.removeProperty(httpSchema.getConcurrentDownload());
+        super.modifyTestElement(config);
+        urlConfigGui.modifyTestElement(config);
+        enableConcurrentDwn();
+
+        HTTPSamplerBaseSchema httpSchema = HTTPSamplerBaseSchema.INSTANCE;
+        if (concurrentDwn.getValue().equals(JEditableCheckBox.Value.of(false))) {
+            // Even though we remove "concurrent download pool size" if the checkbox was unchecked,
+            // we do it on purpose otherwise "merging defaults to regular http sampler" would unexpectedly
+            // override "concurrent download pool size" value
+            // TODO: keep "concurrent download pool size" in defaults, however somehow remove it before merging config
+            //   to the http sampler
             config.removeProperty(httpSchema.getConcurrentDownloadPoolSize());
         }
-        config.set(httpSchema.getStoreAsMD5(), nullIfUnset(useMD5));
-        config.set(httpSchema.getEmbeddedUrlAllowRegex(), embeddedAllowRE.getText());
-        config.set(httpSchema.getEmbeddedUrlExcludeRegex(), embeddedExcludeRE.getText());
 
         if(!StringUtils.isEmpty(sourceIpAddr.getText())) {
-            config.set(httpSchema.getIpSource(), sourceIpAddr.getText());
             config.set(httpSchema.getIpSourceType(), sourceIpType.getSelectedIndex());
         } else {
-            config.removeProperty(httpSchema.getIpSource());
             config.removeProperty(httpSchema.getIpSourceType());
         }
 
-        config.set(httpSchema.getProxy().getScheme(), proxyScheme.getText());
-        config.set(httpSchema.getProxy().getHost(), proxyHost.getText());
-        config.set(httpSchema.getProxy().getPort(), proxyPort.getText());
-        config.set(httpSchema.getProxy().getUsername(), proxyUser.getText());
-        config.set(httpSchema.getProxy().getPassword(), String.valueOf(proxyPass.getPassword()));
         config.set(httpSchema.getImplementation(), String.valueOf(httpImplementation.getSelectedItem()));
-        config.set(httpSchema.getConnectTimeout(), connectTimeOut.getText());
-        config.set(httpSchema.getResponseTimeout(), responseTimeOut.getText());
     }
 
-    /**
-     * Implements JMeterGUIComponent.clearGui
-     */
     @Override
     public void clearGui() {
         super.clearGui();
-        retrieveEmbeddedResources.setSelected(false);
-        concurrentDwn.setSelected(false);
-        concurrentPool.setText(String.valueOf(HTTPSamplerBase.CONCURRENT_POOL_SIZE));
-        enableConcurrentDwn(false);
-        useMD5.setSelected(false);
         urlConfigGui.clear();
-        embeddedAllowRE.setText(""); // $NON-NLS-1$
-        embeddedExcludeRE.setText(""); // $NON-NLS-1$
-        sourceIpAddr.setText(""); // $NON-NLS-1$
-        sourceIpType.setSelectedIndex(HTTPSamplerBase.SourceType.HOSTNAME.ordinal()); //default: IP/Hostname
-        proxyScheme.setText(""); // $NON-NLS-1$
-        proxyHost.setText(""); // $NON-NLS-1$
-        proxyPort.setText(""); // $NON-NLS-1$
-        proxyUser.setText(""); // $NON-NLS-1$
-        proxyPass.setText(""); // $NON-NLS-1$
-        httpImplementation.setSelectedItem(""); // $NON-NLS-1$
-        connectTimeOut.setText(""); // $NON-NLS-1$
-        responseTimeOut.setText(""); // $NON-NLS-1$
     }
 
     @Override
@@ -176,24 +164,10 @@
         super.configure(el);
         AbstractTestElement samplerBase = (AbstractTestElement) el;
         urlConfigGui.configure(el);
-        HTTPSamplerBaseSchema httpSchema = HTTPSamplerBaseSchema.INSTANCE;
-        retrieveEmbeddedResources.setSelected(samplerBase.get(httpSchema.getRetrieveEmbeddedResources()));
-        concurrentDwn.setSelected(samplerBase.get(httpSchema.getConcurrentDownload()));
-        concurrentPool.setText(samplerBase.getPropertyAsString(httpSchema.getConcurrentDownloadPoolSize().getName(), ""));
-        useMD5.setSelected(samplerBase.get(httpSchema.getStoreAsMD5()));
-        embeddedAllowRE.setText(samplerBase.get(httpSchema.getEmbeddedUrlAllowRegex()));
-        embeddedExcludeRE.setText(samplerBase.get(httpSchema.getEmbeddedUrlExcludeRegex()));
-        sourceIpAddr.setText(samplerBase.get(httpSchema.getIpSource()));
-        sourceIpType.setSelectedIndex(samplerBase.get(httpSchema.getIpSourceType()));
 
-        proxyScheme.setText(samplerBase.getString(httpSchema.getProxy().getScheme()));
-        proxyHost.setText(samplerBase.getString(httpSchema.getProxy().getHost()));
-        proxyPort.setText(samplerBase.getString(httpSchema.getProxy().getPort()));
-        proxyUser.setText(samplerBase.getString(httpSchema.getProxy().getUsername()));
-        proxyPass.setText(samplerBase.getString(httpSchema.getProxy().getPassword()));
+        HTTPSamplerBaseSchema httpSchema = HTTPSamplerBaseSchema.INSTANCE;
+        sourceIpType.setSelectedIndex(samplerBase.get(httpSchema.getIpSourceType()));
         httpImplementation.setSelectedItem(samplerBase.getString(httpSchema.getImplementation()));
-        connectTimeOut.setText(samplerBase.getString(httpSchema.getConnectTimeout()));
-        responseTimeOut.setText(samplerBase.getString(httpSchema.getResponseTimeout()));
     }
 
     private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
@@ -268,18 +242,14 @@
 
     protected JPanel createEmbeddedRsrcPanel() {
         // retrieve Embedded resources
-        retrieveEmbeddedResources = new JCheckBox(JMeterUtils.getResString("web_testing_retrieve_images")); // $NON-NLS-1$
         // add a listener to activate or not concurrent dwn.
-        retrieveEmbeddedResources.addItemListener(e -> {
-            if (e.getStateChange() == ItemEvent.SELECTED) { enableConcurrentDwn(true); }
-            else { enableConcurrentDwn(false); }
-        });
+        retrieveEmbeddedResources.addPropertyChangeListener(
+                JEditableCheckBox.VALUE_PROPERTY,
+                ev -> enableConcurrentDwn());
         // Download concurrent resources
-        concurrentDwn = new JCheckBox(JMeterUtils.getResString("web_testing_concurrent_download")); // $NON-NLS-1$
-        concurrentDwn.addItemListener(e -> {
-            if (retrieveEmbeddedResources.isSelected() && e.getStateChange() == ItemEvent.SELECTED) { concurrentPool.setEnabled(true); }
-            else { concurrentPool.setEnabled(false); }
-        });
+        concurrentDwn.addPropertyChangeListener(
+                JEditableCheckBox.VALUE_PROPERTY,
+                ev -> enableConcurrentDwn());
         concurrentPool = new JTextField(2); // 2 columns size
         concurrentPool.setMinimumSize(new Dimension(10, (int) concurrentPool.getPreferredSize().getHeight()));
         concurrentPool.setMaximumSize(new Dimension(60, (int) concurrentPool.getPreferredSize().getHeight()));
@@ -327,12 +297,7 @@
         final JPanel checkBoxPanel = new VerticalPanel();
         checkBoxPanel.setBorder(BorderFactory.createTitledBorder(
                 JMeterUtils.getResString("optional_tasks"))); // $NON-NLS-1$
-
-        // Use MD5
-        useMD5 = new JCheckBox(JMeterUtils.getResString("response_save_as_md5")); // $NON-NLS-1$
-
         checkBoxPanel.add(useMD5);
-
         return checkBoxPanel;
     }
 
@@ -341,11 +306,13 @@
         return getMinimumSize();
     }
 
-    private void enableConcurrentDwn(final boolean enable) {
+    private void enableConcurrentDwn() {
+        boolean enable = !JEditableCheckBox.Value.of(false).equals(retrieveEmbeddedResources.getValue());
         concurrentDwn.setEnabled(enable);
         embeddedAllowRE.setEnabled(enable);
         embeddedExcludeRE.setEnabled(enable);
-        concurrentPool.setEnabled(concurrentDwn.isSelected() && enable);
+        // Allow editing the pool size if "download concurrently" checkbox is set or has expression
+        concurrentPool.setEnabled(enable && !concurrentDwn.getValue().equals(JEditableCheckBox.Value.of(false)));
     }
 
     /**
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/UrlConfigDefaults.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/UrlConfigDefaults.java
index a864fa4..bf704e9 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/UrlConfigDefaults.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/UrlConfigDefaults.java
@@ -118,6 +118,7 @@
      * Return the default HTTP method to be selected in the {@link UrlConfigGui}.
      * @return the default HTTP method to be selected in the {@link UrlConfigGui}
      */
+    @Deprecated
     public String getDefaultMethod() {
         return defaultMethod;
     }
@@ -126,6 +127,7 @@
      * Set the default HTTP method to be selected in the {@link UrlConfigGui}.
      * @param defaultMethod the default HTTP method to be selected in the {@link UrlConfigGui}
      */
+    @Deprecated
     public void setDefaultMethod(String defaultMethod) {
         this.defaultMethod = defaultMethod;
     }
@@ -133,6 +135,7 @@
     /**
      * @return the default value to be set for the followRedirect checkbox in the {@link UrlConfigGui}.
      */
+    @Deprecated
     public boolean isFollowRedirects() {
         return followRedirects;
     }
@@ -141,6 +144,7 @@
      * Set the default value to be set for the followRedirect checkbox in the {@link UrlConfigGui}.
      * @param followRedirects flag whether redirects should be followed
      */
+    @Deprecated
     public void setFollowRedirects(boolean followRedirects) {
         this.followRedirects = followRedirects;
     }
@@ -148,6 +152,7 @@
     /**
      * @return the default value to be set for the autoRedirects checkbox in the {@link UrlConfigGui}.
      */
+    @Deprecated
     public boolean isAutoRedirects() {
         return autoRedirects;
     }
@@ -156,6 +161,7 @@
      * Set the default value to be set for the autoRedirects checkbox in the {@link UrlConfigGui}.
      * @param autoRedirects flag whether redirects should be followed automatically
      */
+    @Deprecated
     public void setAutoRedirects(boolean autoRedirects) {
         this.autoRedirects = autoRedirects;
     }
@@ -163,6 +169,7 @@
     /**
      * @return the default value to be set for the useKeepAlive checkbox in the {@link UrlConfigGui}.
      */
+    @Deprecated
     public boolean isUseKeepAlive() {
         return useKeepAlive;
     }
@@ -171,6 +178,7 @@
      * Set the default value to be set for the useKeepAlive checkbox in the {@link UrlConfigGui}.
      * @param useKeepAlive flag whether to use keep-alive on HTTP requests
      */
+    @Deprecated
     public void setUseKeepAlive(boolean useKeepAlive) {
         this.useKeepAlive = useKeepAlive;
     }
@@ -178,6 +186,7 @@
     /**
      * @return the default value to be set for the useMultipart checkbox in the {@link UrlConfigGui}.
      */
+    @Deprecated
     public boolean isUseMultipart() {
         return useMultipart;
     }
@@ -186,6 +195,7 @@
      * Set the default value to be set for the useMultipart checkbox in the {@link UrlConfigGui}.
      * @param useMultipart flag whether request data should use multi-part feature
      */
+    @Deprecated
     public void setUseMultipart(boolean useMultipart) {
         this.useMultipart = useMultipart;
     }
@@ -193,6 +203,7 @@
     /**
      * @return the default value to be set for the useBrowserCompatibleMultipartMode checkbox in the {@link UrlConfigGui}.
      */
+    @Deprecated
     public boolean isUseBrowserCompatibleMultipartMode() {
         return useBrowserCompatibleMultipartMode;
     }
@@ -201,6 +212,7 @@
      * Set the default value to be set for the useBrowserCompatibleMultipartMode checkbox in the {@link UrlConfigGui}.
      * @param useBrowserCompatibleMultipartMode flag whether to use browser compatible multi-part mode
      */
+    @Deprecated
     public void setUseBrowserCompatibleMultipartMode(boolean useBrowserCompatibleMultipartMode) {
         this.useBrowserCompatibleMultipartMode = useBrowserCompatibleMultipartMode;
     }
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/UrlConfigGui.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/UrlConfigGui.java
index 3906262..e1c09a6 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/UrlConfigGui.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/UrlConfigGui.java
@@ -20,6 +20,7 @@
 import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.FlowLayout;
+import java.util.Arrays;
 
 import javax.swing.BorderFactory;
 import javax.swing.BoxLayout;
@@ -32,7 +33,10 @@
 import org.apache.commons.lang3.StringUtils;
 import org.apache.jmeter.config.Arguments;
 import org.apache.jmeter.config.ConfigTestElement;
+import org.apache.jmeter.gui.BindingGroup;
 import org.apache.jmeter.gui.JBooleanPropertyEditor;
+import org.apache.jmeter.gui.JCheckBoxBinding;
+import org.apache.jmeter.gui.JLabeledFieldBinding;
 import org.apache.jmeter.gui.util.HorizontalPanel;
 import org.apache.jmeter.gui.util.JSyntaxTextArea;
 import org.apache.jmeter.gui.util.JTextScrollPane;
@@ -110,6 +114,8 @@
     private final boolean showRawBodyPane;
     private final boolean showFileUploadPane;
 
+    private final BindingGroup bindingGroup;
+
     /**
      * Constructor which is setup to show HTTP implementation, raw body pane and
      * sampler fields.
@@ -150,21 +156,31 @@
         this.showRawBodyPane = showRawBodyPane;
         this.showFileUploadPane = showFileUploadPane;
         init();
+        HTTPSamplerBaseSchema schema = HTTPSamplerBaseSchema.INSTANCE;
+        bindingGroup = new BindingGroup(
+                Arrays.asList(
+                        new JLabeledFieldBinding(domain, schema.getDomain()),
+                        new JLabeledFieldBinding(port, schema.getPort()),
+                        new JLabeledFieldBinding(protocol, schema.getProtocol()),
+                        new JLabeledFieldBinding(contentEncoding, schema.getContentEncoding()),
+                        new JLabeledFieldBinding(path, schema.getPath())
+                )
+        );
+        if (notConfigOnly) {
+            bindingGroup.addAll(
+                    Arrays.asList(
+                            new JCheckBoxBinding(followRedirects, schema.getFollowRedirects()),
+                            new JCheckBoxBinding(autoRedirects, schema.getAutoRedirects()),
+                            new JLabeledFieldBinding(method, schema.getMethod()),
+                            useKeepAlive,
+                            useMultipart,
+                            useBrowserCompatibleMultipartMode
+                    )
+            );
+        }
     }
 
     public void clear() {
-        domain.setText(""); // $NON-NLS-1$
-        if (notConfigOnly){
-            followRedirects.setSelected(getUrlConfigDefaults().isFollowRedirects());
-            autoRedirects.setSelected(getUrlConfigDefaults().isAutoRedirects());
-            method.setText(getUrlConfigDefaults().getDefaultMethod());
-            useKeepAlive.setBooleanValue(getUrlConfigDefaults().isUseKeepAlive());
-            useBrowserCompatibleMultipartMode.setBooleanValue(getUrlConfigDefaults().isUseBrowserCompatibleMultipartMode());
-        }
-        path.setText(""); // $NON-NLS-1$
-        port.setText(""); // $NON-NLS-1$
-        protocol.setText(""); // $NON-NLS-1$
-        contentEncoding.setText(""); // $NON-NLS-1$
         argsPanel.clear();
         if(showFileUploadPane) {
             filesPanel.clear();
@@ -191,6 +207,7 @@
      * @param element {@link TestElement} to modify
      */
     public void modifyTestElement(TestElement element) {
+        bindingGroup.updateElement(element);
         boolean useRaw = showRawBodyPane && !postBodyContent.getText().isEmpty();
         Arguments args;
         if(useRaw) {
@@ -207,30 +224,21 @@
             arg.setAlwaysEncoded(false);
             args.addArgument(arg);
         } else {
-            args = (Arguments) argsPanel.createTestElement();
+            args = argsPanel.createTestElement();
             HTTPArgument.convertArgumentsToHTTP(args);
         }
         if(showFileUploadPane) {
             filesPanel.modifyTestElement(element);
         }
-        HTTPSamplerBaseSchema.INSTANCE httpSchema = HTTPSamplerBaseSchema.INSTANCE;
+        HTTPSamplerBaseSchema httpSchema = HTTPSamplerBaseSchema.INSTANCE;
         // Treat "unset" checkbox as "property removal" for HTTP Request Defaults component
         // Regular sampler should save both true and false values
         element.set(httpSchema.getPostBodyRaw(), useRaw ? Boolean.TRUE : (notConfigOnly ? false : null));
         element.set(httpSchema.getArguments(), args);
-        element.set(httpSchema.getDomain(), domain.getText());
-        element.set(httpSchema.getPort(), port.getText());
-        element.set(httpSchema.getProtocol(), protocol.getText());
-        element.set(httpSchema.getContentEncoding(), contentEncoding.getText());
-        element.set(httpSchema.getPath(), path.getText());
-        if (notConfigOnly){
-            element.set(httpSchema.getMethod(), method.getText());
-            element.set(httpSchema.getFollowRedirects(), followRedirects.isSelected());
-            element.set(httpSchema.getAutoRedirects(), autoRedirects.isSelected());
-            useKeepAlive.updateElement(element);
-            useMultipart.updateElement(element);
-            useBrowserCompatibleMultipartMode.updateElement(element);
-        }
+    }
+
+    public void assignDefaultValues(TestElement element) {
+        ((HTTPSamplerBase) element).setArguments(argsPanel.createTestElement());
     }
 
     // Just append all the parameter values, and use that as the post body
@@ -270,7 +278,8 @@
      */
     public void configure(TestElement el) {
         setName(el.getName());
-        HTTPSamplerBaseSchema.INSTANCE httpSchema = HTTPSamplerBaseSchema.INSTANCE;
+        bindingGroup.updateUi(el);
+        HTTPSamplerBaseSchema httpSchema = HTTPSamplerBaseSchema.INSTANCE;
         Arguments arguments = el.get(httpSchema.getArguments());
 
         if (showRawBodyPane) {
@@ -291,31 +300,6 @@
         if(showFileUploadPane) {
             filesPanel.configure(el);
         }
-
-        domain.setText(el.getString(httpSchema.getDomain()));
-
-        String portString = el.getString(httpSchema.getPort());
-
-        // Only display the port number if it is meaningfully specified
-        if (portString.equals(HTTPSamplerBase.UNSPECIFIED_PORT_AS_STRING)) {
-            port.setText(""); // $NON-NLS-1$
-        } else {
-            port.setText(portString);
-        }
-        // We explicitly
-        String protocol = el.getPropertyAsString(httpSchema.getProtocol().getName(), "");
-        this.protocol.setText(protocol);
-        String encoding = el.getPropertyAsString(httpSchema.getContentEncoding().getName(), "");
-        contentEncoding.setText(encoding);
-        path.setText(el.getString(httpSchema.getPath()));
-        if (notConfigOnly){
-            method.setText(el.getString(httpSchema.getMethod()));
-            followRedirects.setSelected(el.get(httpSchema.getFollowRedirects()));
-            autoRedirects.setSelected(el.get(httpSchema.getAutoRedirects()));
-            useKeepAlive.updateUi(el);
-            useMultipart.updateUi(el);
-            useBrowserCompatibleMultipartMode.updateUi(el);
-        }
     }
 
     private void init() {// called from ctor, so must not be overridable
@@ -384,35 +368,30 @@
         if (notConfigOnly){
             followRedirects = new JCheckBox(JMeterUtils.getResString("follow_redirects")); // $NON-NLS-1$
             JFactory.small(followRedirects);
-            followRedirects.setSelected(getUrlConfigDefaults().isFollowRedirects());
             followRedirects.addChangeListener(this);
             followRedirects.setVisible(getUrlConfigDefaults().isFollowRedirectsVisible());
 
             autoRedirects = new JCheckBox(JMeterUtils.getResString("follow_redirects_auto")); //$NON-NLS-1$
             JFactory.small(autoRedirects);
             autoRedirects.addChangeListener(this);
-            autoRedirects.setSelected(getUrlConfigDefaults().isAutoRedirects());// Default changed in 2.3 and again in 2.4
             autoRedirects.setVisible(getUrlConfigDefaults().isAutoRedirectsVisible());
 
             useKeepAlive = new JBooleanPropertyEditor(
                     HTTPSamplerBaseSchema.INSTANCE.getUseKeepalive(),
                     JMeterUtils.getResString("use_keepalive"));
             JFactory.small(useKeepAlive);
-            useKeepAlive.setBooleanValue(getUrlConfigDefaults().isUseKeepAlive());
             useKeepAlive.setVisible(getUrlConfigDefaults().isUseKeepAliveVisible());
 
             useMultipart = new JBooleanPropertyEditor(
                     HTTPSamplerBaseSchema.INSTANCE.getUseMultipartPost(),
                     JMeterUtils.getResString("use_multipart_for_http_post")); // $NON-NLS-1$
             JFactory.small(useMultipart);
-            useMultipart.setBooleanValue(getUrlConfigDefaults().isUseMultipart());
             useMultipart.setVisible(getUrlConfigDefaults().isUseMultipartVisible());
 
             useBrowserCompatibleMultipartMode = new JBooleanPropertyEditor(
                     HTTPSamplerBaseSchema.INSTANCE.getUseBrowserCompatibleMultipart(),
                     JMeterUtils.getResString("use_multipart_mode_browser")); // $NON-NLS-1$
             JFactory.small(useBrowserCompatibleMultipartMode);
-            useBrowserCompatibleMultipartMode.setBooleanValue(getUrlConfigDefaults().isUseBrowserCompatibleMultipartMode());
             useBrowserCompatibleMultipartMode.setVisible(getUrlConfigDefaults().isUseBrowserCompatibleMultipartModeVisible());
         }
 
@@ -464,7 +443,7 @@
      * @return a new {@link Arguments} instance associated with the specific GUI used in this component
      */
     protected Arguments createHTTPArgumentsTestElement() {
-        return (Arguments) argsPanel.createTestElement();
+        return argsPanel.createTestElement();
     }
 
     class ValidationTabbedPane extends AbstractValidationTabbedPane {
@@ -507,7 +486,7 @@
          * @return false if one argument has a name
          */
         private boolean canSwitchToRawBodyPane() {
-            Arguments arguments = (Arguments) argsPanel.createTestElement();
+            Arguments arguments = argsPanel.createTestElement();
             for (int i = 0; i < arguments.getArgumentCount(); i++) {
                 if(!StringUtils.isEmpty(arguments.getArgument(i).getName())) {
                     return false;
@@ -542,7 +521,7 @@
      */
     void convertParametersToRaw() {
         if (showRawBodyPane && postBodyContent.getText().isEmpty()) {
-            postBodyContent.setInitialText(computePostBody((Arguments)argsPanel.createTestElement()));
+            postBodyContent.setInitialText(computePostBody(argsPanel.createTestElement()));
             postBodyContent.setCaretPosition(0);
         }
     }
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/GraphQLHTTPSamplerGui.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/GraphQLHTTPSamplerGui.java
index c393e77..d8e2f83 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/GraphQLHTTPSamplerGui.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/GraphQLHTTPSamplerGui.java
@@ -22,6 +22,9 @@
 import org.apache.jmeter.gui.TestElementMetadata;
 import org.apache.jmeter.protocol.http.config.gui.GraphQLUrlConfigGui;
 import org.apache.jmeter.protocol.http.config.gui.UrlConfigGui;
+import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBaseSchema;
+import org.apache.jmeter.protocol.http.util.HTTPConstants;
+import org.apache.jmeter.testelement.TestElement;
 import org.apache.jmeter.util.JMeterUtils;
 
 /**
@@ -33,6 +36,15 @@
 
     private static final long serialVersionUID = 1L;
 
+    @Override
+    public void assignDefaultValues(TestElement element) {
+        super.assignDefaultValues(element);
+        HTTPSamplerBaseSchema schema = HTTPSamplerBaseSchema.INSTANCE;
+        element.set(schema.getMethod(), HTTPConstants.POST);
+        element.set(schema.getUseBrowserCompatibleMultipart(), false);
+        element.set(schema.getUseMultipartPost(), false);
+    }
+
     public GraphQLHTTPSamplerGui() {
         super();
     }
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui.java
index 3459a73..ca7234f 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui.java
@@ -19,10 +19,9 @@
 
 import java.awt.BorderLayout;
 import java.awt.Dimension;
-import java.awt.event.ItemEvent;
+import java.util.Arrays;
 
 import javax.swing.BorderFactory;
-import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
@@ -31,7 +30,10 @@
 import javax.swing.JTabbedPane;
 import javax.swing.JTextField;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.jmeter.gui.GUIMenuSortOrder;
+import org.apache.jmeter.gui.JBooleanPropertyEditor;
+import org.apache.jmeter.gui.JTextComponentBinding;
 import org.apache.jmeter.gui.TestElementMetadata;
 import org.apache.jmeter.gui.util.HorizontalPanel;
 import org.apache.jmeter.gui.util.VerticalPanel;
@@ -40,9 +42,11 @@
 import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBaseSchema;
 import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory;
 import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
+import org.apache.jmeter.protocol.http.util.HTTPConstants;
 import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
 import org.apache.jmeter.testelement.TestElement;
 import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jorphan.gui.JEditableCheckBox;
 import org.apache.jorphan.gui.JFactory;
 
 import net.miginfocom.swing.MigLayout;
@@ -57,10 +61,16 @@
     private static final long serialVersionUID = 242L;
 
     private UrlConfigGui urlConfigGui;
-    private JCheckBox retrieveEmbeddedResources;
-    private JCheckBox concurrentDwn;
+    private final JBooleanPropertyEditor retrieveEmbeddedResources = new JBooleanPropertyEditor(
+            HTTPSamplerBaseSchema.INSTANCE.getRetrieveEmbeddedResources(),
+            JMeterUtils.getResString("web_testing_retrieve_images"));
+    private final JBooleanPropertyEditor concurrentDwn = new JBooleanPropertyEditor(
+            HTTPSamplerBaseSchema.INSTANCE.getConcurrentDownload(),
+            JMeterUtils.getResString("web_testing_concurrent_download"));
     private JTextField concurrentPool;
-    private JCheckBox useMD5;
+    private final JBooleanPropertyEditor useMD5 = new JBooleanPropertyEditor(
+            HTTPSamplerBaseSchema.INSTANCE.getStoreAsMD5(),
+            JMeterUtils.getResString("response_save_as_md5")); // $NON-NLS-1$
     private JTextField embeddedAllowRE; // regular expression used to match against embedded resource URLs to allow
     private JTextField embeddedExcludeRE; // regular expression used to match against embedded resource URLs to exclude
     private JTextField sourceIpAddr; // does not apply to Java implementation
@@ -77,14 +87,40 @@
     private final boolean isAJP;
 
     public HttpTestSampleGui() {
-        isAJP = false;
-        init();
+        this(false);
     }
 
     // For use by AJP
     protected HttpTestSampleGui(boolean ajp) {
         isAJP = ajp;
         init();
+        HTTPSamplerBaseSchema schema = HTTPSamplerBaseSchema.INSTANCE;
+        bindingGroup.addAll(
+                Arrays.asList(
+                        retrieveEmbeddedResources,
+                        concurrentDwn,
+                        new JTextComponentBinding(concurrentPool, schema.getConcurrentDownloadPoolSize()),
+                        useMD5,
+                        new JTextComponentBinding(embeddedAllowRE, schema.getEmbeddedUrlAllowRegex()),
+                        new JTextComponentBinding(embeddedExcludeRE, schema.getEmbeddedUrlExcludeRegex())
+                )
+        );
+        if (!isAJP) {
+            bindingGroup.addAll(
+                    Arrays.asList(
+                            new JTextComponentBinding(sourceIpAddr, schema.getIpSource()),
+                            // TODO: sourceIpType
+                            new JTextComponentBinding(proxyScheme, schema.getProxy().getScheme()),
+                            new JTextComponentBinding(proxyHost, schema.getProxy().getHost()),
+                            new JTextComponentBinding(proxyPort, schema.getProxy().getPort()),
+                            new JTextComponentBinding(proxyUser, schema.getProxy().getUsername()),
+                            new JTextComponentBinding(proxyPass, schema.getProxy().getPassword()),
+                            // TODO: httpImplementation
+                            new JTextComponentBinding(connectTimeOut, schema.getConnectTimeout()),
+                            new JTextComponentBinding(responseTimeOut, schema.getResponseTimeout())
+                    )
+            );
+        }
     }
 
     /**
@@ -96,24 +132,9 @@
         final HTTPSamplerBase samplerBase = (HTTPSamplerBase) element;
         HTTPSamplerBaseSchema httpSchema = HTTPSamplerBaseSchema.INSTANCE;
         urlConfigGui.configure(element);
-        retrieveEmbeddedResources.setSelected(samplerBase.isImageParser());
-        concurrentDwn.setSelected(samplerBase.isConcurrentDwn());
-        concurrentPool.setText(samplerBase.getConcurrentPool());
-        useMD5.setSelected(samplerBase.useMD5());
-        embeddedAllowRE.setText(samplerBase.getEmbeddedUrlRE());
-        embeddedExcludeRE.setText(samplerBase.getEmbededUrlExcludeRE());
         if (!isAJP) {
-            sourceIpAddr.setText(samplerBase.getIpSource());
             sourceIpType.setSelectedIndex(samplerBase.getIpSourceType());
-
-            proxyScheme.setText(samplerBase.getString(httpSchema.getProxy().getScheme()));
-            proxyHost.setText(samplerBase.getString(httpSchema.getProxy().getHost()));
-            proxyPort.setText(samplerBase.getString(httpSchema.getProxy().getPort()));
-            proxyUser.setText(samplerBase.getString(httpSchema.getProxy().getUsername()));
-            proxyPass.setText(samplerBase.getString(httpSchema.getProxy().getPassword()));
             httpImplementation.setSelectedItem(samplerBase.getString(httpSchema.getImplementation()));
-            connectTimeOut.setText(samplerBase.getString(httpSchema.getConnectTimeout()));
-            responseTimeOut.setText(samplerBase.getString(httpSchema.getResponseTimeout()));
         }
     }
 
@@ -121,10 +142,19 @@
      * {@inheritDoc}
      */
     @Override
-    public TestElement createTestElement() {
-        HTTPSamplerBase sampler = new HTTPSamplerProxy();
-        modifyTestElement(sampler);
-        return sampler;
+    public TestElement makeTestElement() {
+        return new HTTPSamplerProxy();
+    }
+
+    @Override
+    public void assignDefaultValues(TestElement element) {
+        super.assignDefaultValues(element);
+        HTTPSamplerBaseSchema schema = HTTPSamplerBaseSchema.INSTANCE;
+        // It probably does not make much sense overriding HTTP method with HTTP Request Defaults, so we set it here
+        element.set(schema.getMethod(), HTTPConstants.GET);
+        element.set(schema.getFollowRedirects(), true);
+        element.set(schema.getUseKeepalive(), true);
+        urlConfigGui.assignDefaultValues(element);
     }
 
     /**
@@ -134,31 +164,19 @@
      */
     @Override
     public void modifyTestElement(TestElement sampler) {
-        sampler.clear();
+        super.modifyTestElement(sampler);
         urlConfigGui.modifyTestElement(sampler);
         final HTTPSamplerBase samplerBase = (HTTPSamplerBase) sampler;
         HTTPSamplerBaseSchema httpSchema = samplerBase.getSchema();
-        samplerBase.setImageParser(retrieveEmbeddedResources.isSelected());
-        enableConcurrentDwn(retrieveEmbeddedResources.isSelected());
-        samplerBase.setConcurrentDwn(concurrentDwn.isSelected());
-        samplerBase.setConcurrentPool(concurrentPool.getText());
-        samplerBase.setMD5(useMD5.isSelected());
-        samplerBase.setEmbeddedUrlRE(embeddedAllowRE.getText());
-        samplerBase.setEmbeddedUrlExcludeRE(embeddedExcludeRE.getText());
+        enableConcurrentDwn();
         if (!isAJP) {
-            samplerBase.setIpSource(sourceIpAddr.getText());
-            samplerBase.setIpSourceType(sourceIpType.getSelectedIndex());
-
-            samplerBase.set(httpSchema.getProxy().getScheme(), proxyScheme.getText());
-            samplerBase.set(httpSchema.getProxy().getHost(), proxyHost.getText());
-            samplerBase.set(httpSchema.getProxy().getPort(), proxyPort.getText());
-            samplerBase.set(httpSchema.getProxy().getUsername(), proxyUser.getText());
-            samplerBase.set(httpSchema.getProxy().getPassword(), String.valueOf(proxyPass.getPassword()));
+            if (!StringUtils.isEmpty(sourceIpAddr.getText())) {
+                samplerBase.set(httpSchema.getIpSourceType(), sourceIpType.getSelectedIndex());
+            } else {
+                samplerBase.removeProperty(httpSchema.getIpSourceType());
+            }
             samplerBase.set(httpSchema.getImplementation(), String.valueOf(httpImplementation.getSelectedItem()));
-            samplerBase.set(httpSchema.getConnectTimeout(), connectTimeOut.getText());
-            samplerBase.set(httpSchema.getResponseTimeout(), responseTimeOut.getText());
         }
-        super.configureTestElement(sampler);
     }
 
     /**
@@ -279,18 +297,14 @@
 
     protected JPanel createEmbeddedRsrcPanel() {
         // retrieve Embedded resources
-        retrieveEmbeddedResources = new JCheckBox(JMeterUtils.getResString("web_testing_retrieve_images")); // $NON-NLS-1$
         // add a listener to activate or not concurrent dwn.
-        retrieveEmbeddedResources.addItemListener(e -> {
-            if (e.getStateChange() == ItemEvent.SELECTED) { enableConcurrentDwn(true); }
-            else { enableConcurrentDwn(false); }
-        });
+        retrieveEmbeddedResources.addPropertyChangeListener(
+                JEditableCheckBox.VALUE_PROPERTY,
+                ev -> enableConcurrentDwn());
         // Download concurrent resources
-        concurrentDwn = new JCheckBox(JMeterUtils.getResString("web_testing_concurrent_download")); // $NON-NLS-1$
-        concurrentDwn.addItemListener(e -> {
-            if (retrieveEmbeddedResources.isSelected() && e.getStateChange() == ItemEvent.SELECTED) { concurrentPool.setEnabled(true); }
-            else { concurrentPool.setEnabled(false); }
-        });
+        concurrentDwn.addPropertyChangeListener(
+                JEditableCheckBox.VALUE_PROPERTY,
+                ev -> enableConcurrentDwn());
         concurrentPool = new JTextField(2); // 2 column size
         concurrentPool.setMinimumSize(new Dimension(10, (int) concurrentPool.getPreferredSize().getHeight()));
         concurrentPool.setMaximumSize(new Dimension(60, (int) concurrentPool.getPreferredSize().getHeight()));
@@ -341,8 +355,6 @@
         checkBoxPanel.setBorder(BorderFactory.createTitledBorder(
                 JMeterUtils.getResString("optional_tasks"))); // $NON-NLS-1$
 
-        // Use MD5
-        useMD5 = new JCheckBox(JMeterUtils.getResString("response_save_as_md5")); // $NON-NLS-1$
         checkBoxPanel.add(useMD5);
 
         return checkBoxPanel;
@@ -370,38 +382,19 @@
         return getMinimumSize();
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public void clearGui() {
         super.clearGui();
-        retrieveEmbeddedResources.setSelected(false);
-        concurrentDwn.setSelected(false);
-        concurrentPool.setText(String.valueOf(HTTPSamplerBase.CONCURRENT_POOL_SIZE));
-        enableConcurrentDwn(false);
-        useMD5.setSelected(false);
         urlConfigGui.clear();
-        embeddedAllowRE.setText(""); // $NON-NLS-1$
-        if (!isAJP) {
-            sourceIpAddr.setText(""); // $NON-NLS-1$
-            sourceIpType.setSelectedIndex(HTTPSamplerBase.SourceType.HOSTNAME.ordinal()); //default: IP/Hostname
-            proxyScheme.setText(""); // $NON-NLS-1$
-            proxyHost.setText(""); // $NON-NLS-1$
-            proxyPort.setText(""); // $NON-NLS-1$
-            proxyUser.setText(""); // $NON-NLS-1$
-            proxyPass.setText(""); // $NON-NLS-1$
-            httpImplementation.setSelectedItem(""); // $NON-NLS-1$
-            connectTimeOut.setText(""); // $NON-NLS-1$
-            responseTimeOut.setText(""); // $NON-NLS-1$
-        }
     }
 
-    private void enableConcurrentDwn(boolean enable) {
+    private void enableConcurrentDwn() {
+        boolean enable = !JEditableCheckBox.Value.of(false).equals(retrieveEmbeddedResources.getValue());
         concurrentDwn.setEnabled(enable);
         embeddedAllowRE.setEnabled(enable);
         embeddedExcludeRE.setEnabled(enable);
-        concurrentPool.setEnabled(concurrentDwn.isSelected() && enable);
+        // Allow editing the pool size if "download concurrently" checkbox is set or has expression
+        concurrentPool.setEnabled(enable && !concurrentDwn.getValue().equals(JEditableCheckBox.Value.of(false)));
     }
 
 
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/HTTPArgumentsPanel.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/HTTPArgumentsPanel.java
index f087afe..1f4af2b 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/HTTPArgumentsPanel.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/HTTPArgumentsPanel.java
@@ -104,10 +104,10 @@
     }
 
     @Override
-    public TestElement createTestElement() {
+    public Arguments createTestElement() {
         Arguments args = getUnclonedParameters();
-        super.configureTestElement(args);
-        return (TestElement) args.clone();
+        assignDefaultValues(args);
+        return (Arguments) args.clone();
     }
 
     /**
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java
index 12db65f..2818fc8 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java
@@ -315,11 +315,12 @@
         if (StringUtils.isNotEmpty(url.getQuery())) {
             path += "?" + url.getQuery();
         }
+        // setMethod must be before setPath as setPath uses method to determine if parameters should be parsed or not
+        httpSampler.setMethod(request.getMethod());
         httpSampler.setPath(path);
         httpSampler.setDomain(url.getHost());
         httpSampler.setUseKeepAlive(request.isKeepAlive());
         httpSampler.setFollowRedirects(true);
-        httpSampler.setMethod(request.getMethod());
         HeaderManager headerManager = createHeaderManager(request);
         httpSampler.addTestElement(headerManager);
         configureTimeout(request, httpSampler);
diff --git a/src/protocol/http/src/main/kotlin/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBaseSchema.kt b/src/protocol/http/src/main/kotlin/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBaseSchema.kt
index 587d9ed..5f2a317 100644
--- a/src/protocol/http/src/main/kotlin/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBaseSchema.kt
+++ b/src/protocol/http/src/main/kotlin/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBaseSchema.kt
@@ -65,7 +65,7 @@
         by testElement("HTTPSampler.dns_cache_manager")
 
     public val method: StringPropertyDescriptor<HTTPSamplerBaseSchema>
-        by string("HTTPSampler.method")
+        by string("HTTPSampler.method", default = HTTPSamplerBase.DEFAULT_METHOD)
 
     public val protocol: StringPropertyDescriptor<HTTPSamplerBaseSchema>
         by string("HTTPSampler.protocol", default = HTTPConstants.PROTOCOL_HTTP)
diff --git a/src/protocol/http/src/test/kotlin/org/apache/jmeter/protocol/http/sampler/HttpSamplerPrintDslTest.kt b/src/protocol/http/src/test/kotlin/org/apache/jmeter/protocol/http/sampler/HttpSamplerPrintDslTest.kt
index d934844..0888218 100644
--- a/src/protocol/http/src/test/kotlin/org/apache/jmeter/protocol/http/sampler/HttpSamplerPrintDslTest.kt
+++ b/src/protocol/http/src/test/kotlin/org/apache/jmeter/protocol/http/sampler/HttpSamplerPrintDslTest.kt
@@ -92,6 +92,8 @@
             +element
         }
 
+        // "arguments" property is assigned in HTTPSamplerBase constructor, so it comes before
+        // name and guiClass common properties
         assertEquals(
             """
             org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy::class {
@@ -103,17 +105,19 @@
                             it[testClass] = "org.apache.jmeter.config.Arguments"
                         }
                     }
+                    it[name] = "HTTP Request"
+                    it[guiClass] = "org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui"
                     it[method] = "GET"
                     it[followRedirects] = true
                     it[useKeepalive] = true
-                    it[implementation] = "HttpClient4"
-                    it[name] = "HTTP Request"
-                    it[guiClass] = "org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui"
                 }
             }
 
             """.trimIndent().replace("\r\n", "\n"),
-            DslPrinterTraverser().also { tree.traverse(it) }.toString().replace("\r\n", "\n")
+            DslPrinterTraverser().also { tree.traverse(it) }.toString().replace("\r\n", "\n"),
+            "HTTP request created with HttpTestSampleGui.createTestElement() should have expected output shape. " +
+                "DslPrinterTraverser does not print the values which are automatically assigned in constructor, " +
+                "so the expected output does not have it[testClass] = HTTPSamplerProxy, and empty list in arguments"
         )
     }
 
@@ -130,18 +134,15 @@
                             it[testClass] = "org.apache.jmeter.config.Arguments"
                         }
                     }
+                    it[name] = "HTTP Request"
+                    it[guiClass] = "org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui"
                     it[method] = "GET"
                     it[followRedirects] = true
                     it[useKeepalive] = true
-                    it[implementation] = "HttpClient4"
-                    it[name] = "HTTP Request"
-                    it[guiClass] = "org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui"
                 }
             }
         }.keys.first() as TestElement
 
-        createdWithUi.traverse(RemoveDefaultValues)
-
         // We compare elements manually, and call assertEquals(toString, toString) so
         // the test output looks better (diff in IDE) in case of the failure
         // If we use just assertEquals(createdWithUi, createdWithDsl), then there will be no "diff in IDE"
diff --git a/xdocs/usermanual/jmeter_tutorial.xml b/xdocs/usermanual/jmeter_tutorial.xml
index 1f7ff28..814cf30 100644
--- a/xdocs/usermanual/jmeter_tutorial.xml
+++ b/xdocs/usermanual/jmeter_tutorial.xml
@@ -259,6 +259,10 @@
 (and possibly translations as well).</li>
     </ol>
   </li>
+  <li>Override <code>org.apache.jmeter.gui.JMeterGUIComponent.makeTestElement</code> method so it returns
+  the appropriate <code>TestElement</code>. JMeter will use <code>makeTestElement</code> when user creates the element
+  from the UI. In most cases it should be just creating the test element like
+  <code>return new SetupThreadGroup()</code>.</li>
   <li>Create your GUI. Whatever style you like, layout your GUI. Your class ultimately extends
     <code>JPanel</code>, so your layout must be in your class's own <code>ContentPane</code>.
     Do not hook up GUI elements to your <code>TestElement</code> class via actions and events.
@@ -287,10 +291,15 @@
       </li>
     </ol>
   </li>
-  <li>Implement <code>public void configure(TestElement el)</code>
+  <li>Then you need to wire UI elements with the properties of the new <code>TestElement</code>. If you create
+  <code>TestElementSchema</code> for your test element (see <code>ThreadGroupSchema</code>), then you could use
+  automatic wiring with <code>PropertyEditorCollection</code></li>
+  <li>If you do not use schema for wiring properties to UI control, or if you have non-trivial controls,
+    you might customize <code>TestElement</code> properties to UI control mapping by overriding <code>public void configure(TestElement el)</code>
     <ol>
       <li>Be sure to call <code>super.configure(e)</code>. This will populate some of the data for you, like
-         the name of the element.</li>
+      the name of the element. Note: JMeter reuses UI elements when user changes the active element in test tree,
+      so you need to set all the text fields in <code>configure</code> method to avoid displaying stale contents.</li>
       <li>Use this method to set data into your GUI elements. Example:
 <source>
 public void configure(TestElement el) {
@@ -312,16 +321,18 @@
 }
 </source>
       </li>
-      <li>Implement <code>public void modifyTestElement(TestElement e)</code>. This is where you
-         move the data from your GUI elements to the <code>TestElement</code>. It is the logical reverse of the
-         previous method.
+      <li>If you do not use schema for wiring UI controls to <code>TestElement</code> properties,
+         or if you want customized behavior, you might override <code>public void modifyTestElement(TestElement e)</code>.
+         It is the logical reverse of <code>configure</code> method.
          <ol>
-           <li>Call <code>super.configureTestElement(e)</code>. This will take care of some default data for
+           <li>Call <code>super.modifyTestElement(e)</code>. This will take care of some default data for
              you.</li>
+           <li>Note: in most cases, you want to treat "empty field" as "absent property", so make sure to
+           remove the property if the input field is empty.</li>
            <li>Example:
 <source>
 public void modifyTestElement(TestElement e) {
-    super.configureTestElement(e);
+    super.modifyTestElement(e);
     e.setProperty(new BooleanProperty(
             RegexExtractor.USEHEADERS,
             useHeaders.isSelected()));
@@ -339,16 +350,9 @@
            </li>
          </ol>
        </li>
-       <li>Implement <code>public TestElement createTestElement()</code>. This method should create a
-         new instance of your <code>TestElement</code> class, and then pass it to the <code>modifyTestElement(TestElement)</code>
-         method you made above
-<source>
-public TestElement createTestElement() {
-    RegexExtractor extractor = new RegexExtractor();
-    modifyTestElement(extractor);
-    return extractor;
-}
-</source>
+       <li>If your UI includes controls that do not map to <code>TestElement</code> properties (sliders, tabs),
+         then you might want to reset them when user switches the controls. You can do that by overriding
+         <code>clearGui()</code> method and resetting the controls there.
        </li>
     </ol>
   </li>