You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by fs...@apache.org on 2017/02/11 17:04:07 UTC

svn commit: r1782622 - in /jmeter/trunk: bin/ src/core/org/apache/jmeter/resources/ src/core/org/apache/jmeter/save/ src/protocol/http/org/apache/jmeter/protocol/http/control/ src/protocol/http/org/apache/jmeter/protocol/http/gui/ test/src/org/apache/j...

Author: fschumacher
Date: Sat Feb 11 17:04:06 2017
New Revision: 1782622

URL: http://svn.apache.org/viewvc?rev=1782622&view=rev
Log:
Add a table of static hosts to the DNS Cache Manger. This resolves the problem,
that host header gets ignored when doing basic authentication in HC4.

Bugzilla Id: 59174

Added:
    jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/StaticHost.java
Modified:
    jmeter/trunk/bin/saveservice.properties
    jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties
    jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties
    jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java
    jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/DNSCacheManager.java
    jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/gui/DNSCachePanel.java
    jmeter/trunk/test/src/org/apache/jmeter/protocol/http/control/TestDNSCacheManager.java
    jmeter/trunk/xdocs/changes.xml
    jmeter/trunk/xdocs/images/screenshots/dns-cache-manager.png
    jmeter/trunk/xdocs/usermanual/component_reference.xml

Modified: jmeter/trunk/bin/saveservice.properties
URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/saveservice.properties?rev=1782622&r1=1782621&r2=1782622&view=diff
==============================================================================
--- jmeter/trunk/bin/saveservice.properties (original)
+++ jmeter/trunk/bin/saveservice.properties Sat Feb 11 17:04:06 2017
@@ -294,6 +294,7 @@ SoapSamplerGui=org.apache.jmeter.protoco
 SplineVisualizer=org.apache.jmeter.visualizers.SplineVisualizer
 # Originally deleted in r397955 as class is obsolete; needed for compat.
 SqlConfigGui=org.apache.jmeter.protocol.jdbc.config.gui.SqlConfigGui
+StaticHost=org.apache.jmeter.protocol.http.control.StaticHost
 StatGraphVisualizer=org.apache.jmeter.visualizers.StatGraphVisualizer
 StatVisualizer=org.apache.jmeter.visualizers.StatVisualizer
 SubscriberSampler=org.apache.jmeter.protocol.jms.sampler.SubscriberSampler

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=1782622&r1=1782621&r2=1782622&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties Sat Feb 11 17:04:06 2017
@@ -23,6 +23,7 @@
 about=About Apache JMeter
 active_threads_tooltip=Running threads
 add=Add
+add_host=Add static host
 add_as_child=Add as Child
 add_from_clipboard=Add from Clipboard
 add_from_suggested_excludes=Add suggested Excludes
@@ -249,6 +250,7 @@ delay=Startup delay (seconds)
 delayed_start=Delay Thread creation until needed
 delete=Delete
 delete_parameter=Delete Variable
+delete_host=Delete static host
 delete_test=Delete Test
 delete_user=Delete User
 deltest=Deletion test
@@ -260,6 +262,7 @@ disable=Disable
 dn=DN
 dns_cache_manager_title=DNS Cache Manager
 dns_hostname_or_ip=Hostname or IP address
+dns_hosts=Static Hosttable
 dns_servers=DNS Servers
 domain=Domain
 done=Done
@@ -1348,4 +1351,4 @@ xpath_tidy_report_errors=Report errors
 xpath_tidy_show_warnings=Show warnings
 you_must_enter_a_valid_number=You must enter a valid number
 zh_cn=Chinese (Simplified)
-zh_tw=Chinese (Traditional)
\ No newline at end of file
+zh_tw=Chinese (Traditional)

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=1782622&r1=1782621&r2=1782622&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 Sat Feb 11 17:04:06 2017
@@ -22,6 +22,7 @@ add=Ajouter
 add_as_child=Ajouter en tant qu'enfant
 add_from_clipboard=Ajouter depuis Presse-papier
 add_from_suggested_excludes=Ajouter exclusions propos\u00E9es
+add_host=Ajouter un h\u00F4te statique
 add_parameter=Ajouter un param\u00E8tre
 add_pattern=Ajouter un motif \:
 add_test=Ajout
@@ -243,6 +244,7 @@ default_value_field=Valeur par d\u00E9fa
 delay=D\u00E9lai avant d\u00E9marrage (secondes) \:
 delayed_start=Cr\u00E9er les unit\u00E9s seulement quand n\u00E9cessaire
 delete=Supprimer
+delete_host=Supprimer l''h\u00F4te statique
 delete_parameter=Supprimer le param\u00E8tre
 delete_test=Suppression
 delete_user=Supprimer l'utilisateur
@@ -255,6 +257,7 @@ disable=D\u00E9sactiver
 dn=Racine DN \:
 dns_cache_manager_title=Gestionnaire de cache DNS
 dns_hostname_or_ip=Nom de machine ou adresse IP
+dns_hosts=Table d''h\u00F4te statique
 dns_servers=Serveurs DNS
 domain=Domaine \:
 done=Fait

Modified: jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java?rev=1782622&r1=1782621&r2=1782622&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java Sat Feb 11 17:04:06 2017
@@ -154,7 +154,7 @@ public class SaveService {
     private static String fileVersion = ""; // computed from saveservice.properties file// $NON-NLS-1$
     // Must match the sha1 checksum of the file saveservice.properties (without newline character),
     // used to ensure saveservice.properties and SaveService are updated simultaneously
-    static final String FILEVERSION = "2b8bbf6ee18f324d63d4c69981561fd9e125dd99"; // Expected value $NON-NLS-1$
+    static final String FILEVERSION = "09831d0774c9e7131205e2f5c614abe0c3effba6"; // Expected value $NON-NLS-1$
 
     private static String fileEncoding = ""; // read from properties file// $NON-NLS-1$
 

Modified: jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/DNSCacheManager.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/DNSCacheManager.java?rev=1782622&r1=1782621&r2=1782622&view=diff
==============================================================================
--- jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/DNSCacheManager.java (original)
+++ jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/DNSCacheManager.java Sat Feb 11 17:04:06 2017
@@ -23,6 +23,7 @@ import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.http.conn.DnsResolver;
@@ -33,6 +34,9 @@ import org.apache.jmeter.testelement.Tes
 import org.apache.jmeter.testelement.property.BooleanProperty;
 import org.apache.jmeter.testelement.property.CollectionProperty;
 import org.apache.jmeter.testelement.property.JMeterProperty;
+import org.apache.jmeter.testelement.property.NullProperty;
+import org.apache.jmeter.testelement.property.PropertyIterator;
+import org.apache.jmeter.testelement.property.TestElementProperty;
 import org.apache.jmeter.threads.JMeterContextService;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
@@ -57,7 +61,8 @@ import org.xbill.DNS.Type;
  */
 
 public class DNSCacheManager extends ConfigTestElement implements TestIterationListener, Serializable, DnsResolver {
-    private static final long serialVersionUID = 2121L;
+
+    private static final long serialVersionUID = 2122L;
 
     private static final Logger log = LoggerFactory.getLogger(DNSCacheManager.class);
 
@@ -68,6 +73,8 @@ public class DNSCacheManager extends Con
 
     private static final String SERVERS = "DNSCacheManager.servers"; // $NON-NLS-1$
 
+    private  static final String HOSTS = "DNSCacheManager.hosts"; // $NON-NLS-1$
+
     private static final String IS_CUSTOM_RESOLVER = "DNSCacheManager.isCustomResolver"; // $NON-NLS-1$
     //-- JMX tag values
 
@@ -142,7 +149,7 @@ public class DNSCacheManager extends Con
     @Override
     public InetAddress[] resolve(String host) throws UnknownHostException {
         InetAddress[] result = cache.get(host);
-        // cache may contain
+        // cache may contain null.
         // A return value of null does not necessarily 
         // indicate that the map contains no mapping 
         // for the key; it's also possible that the map 
@@ -154,6 +161,14 @@ public class DNSCacheManager extends Con
                         + Arrays.toString(result));
             }
             return result;
+        } else if (isStaticHost(host)) {
+            InetAddress[] staticAddresses = fromStaticHost(host);
+            if (log.isDebugEnabled()) {
+                log.debug("Cache miss thr#{}: (static) {} => {}", JMeterContextService.getContext().getThreadNum(), host,
+                        Arrays.toString(staticAddresses));
+            }
+            cache.put(host, staticAddresses);
+            return staticAddresses;
         } else {
             InetAddress[] addresses = requestLookup(host);
             if (log.isDebugEnabled()) {
@@ -165,6 +180,56 @@ public class DNSCacheManager extends Con
         }
     }
 
+    private boolean isStaticHost(String host) {
+        JMeterProperty p = getProperty(HOSTS);
+        if (p instanceof NullProperty) {
+            removeProperty(HOSTS);
+            return false;
+        }
+        CollectionProperty property = (CollectionProperty) p;
+        PropertyIterator iterator = property.iterator();
+        while (iterator.hasNext()) {
+            TestElementProperty possibleEntry = (TestElementProperty) iterator.next();
+            if (log.isDebugEnabled()) {
+                log.debug("Look for {} at {}: {}", host, possibleEntry.getObjectValue(), possibleEntry.getObjectValue().getClass());
+            }
+            StaticHost entry = (StaticHost) possibleEntry.getObjectValue();
+            if (entry.getName().equalsIgnoreCase(host)) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Found static host: {} => {}", host, entry.getAddress());
+                }
+                return true;
+            }
+        }
+        log.debug("No static host found for {}", host);
+        return false;
+    }
+
+    private InetAddress[] fromStaticHost(String host) {
+        JMeterProperty p = getProperty(HOSTS);
+        if (p instanceof NullProperty) {
+            removeProperty(HOSTS);
+            return new InetAddress[0];
+        }
+        CollectionProperty property = (CollectionProperty) p;
+        PropertyIterator iterator = property.iterator();
+        while (iterator.hasNext()) {
+            StaticHost entry = (StaticHost) ((TestElementProperty)iterator.next()).getObjectValue();
+            if (entry.getName().equals(host)) {
+                List<InetAddress> addresses = new ArrayList<>();
+                for (String address : Arrays.asList(entry.getAddress().split("\\s*,\\s*"))) {
+                    try {
+                        addresses.addAll(Arrays.asList(requestLookup(address)));
+                    } catch (UnknownHostException e) {
+                        log.warn("Couldn't resolve static address {} for host {}", address, host, e);
+                    }
+                }
+                return addresses.toArray(new InetAddress[addresses.size()]);
+            }
+        }
+        return new InetAddress[0];
+    }
+
     /**
      * Sends DNS request via system or custom DNS resolver
      * @param host
@@ -237,6 +302,7 @@ public class DNSCacheManager extends Con
     public void clear() {
         super.clear();
         clearServers(); // ensure data is set up OK initially
+        clearHosts();
         this.cache.clear();
         this.initFailed = false;
         this.resolver = null;
@@ -258,6 +324,24 @@ public class DNSCacheManager extends Con
         return (CollectionProperty) getProperty(SERVERS);
     }
 
+    private void clearHosts() {
+        log.debug("Clear all hosts from store");
+        removeProperty(HOSTS);
+        cache.clear();
+    }
+
+    public void addHost(String dnsHost, String addresses) {
+        getHosts().addItem(new StaticHost(dnsHost, addresses));
+        cache.clear();
+    }
+
+    public CollectionProperty getHosts() {
+        if (getProperty(HOSTS) instanceof NullProperty) {
+            setProperty(new CollectionProperty(HOSTS, new ArrayList<StaticHost>()));
+        }
+        return (CollectionProperty) getProperty(HOSTS);
+    }
+
     /**
      * Clean DNS cache each iteration
      * 

Added: jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/StaticHost.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/StaticHost.java?rev=1782622&view=auto
==============================================================================
--- jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/StaticHost.java (added)
+++ jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/StaticHost.java Sat Feb 11 17:04:06 2017
@@ -0,0 +1,62 @@
+/*
+ * 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.protocol.http.control;
+
+import java.io.Serializable;
+
+import org.apache.jmeter.testelement.AbstractTestElement;
+
+public class StaticHost extends AbstractTestElement implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String SNAME = "StaticHost.Name";
+    private static final String SADDRESS = "StaticHost.Address";
+
+    public StaticHost() {
+        this("", "");
+    }
+
+    public StaticHost(String name, String address) {
+        setProperty(SNAME, name);
+        setProperty(SADDRESS, address);
+    }
+
+    @Override
+    public void setName(String name) {
+        setProperty(SNAME, name);
+    }
+
+    @Override
+    public String getName() {
+        return getPropertyAsString(SNAME);
+    }
+
+    public void setAddress(String address) {
+        setProperty(SADDRESS, address);
+    }
+
+    public String getAddress() {
+        return getPropertyAsString(SADDRESS);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("StaticHost(%s, %s)", getName(), getAddress());
+    }
+}

Modified: jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/gui/DNSCachePanel.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/gui/DNSCachePanel.java?rev=1782622&r1=1782621&r2=1782622&view=diff
==============================================================================
--- jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/gui/DNSCachePanel.java (original)
+++ jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/gui/DNSCachePanel.java Sat Feb 11 17:04:06 2017
@@ -36,11 +36,14 @@ import javax.swing.ListSelectionModel;
 import org.apache.jmeter.config.gui.AbstractConfigGui;
 import org.apache.jmeter.gui.util.PowerTableModel;
 import org.apache.jmeter.protocol.http.control.DNSCacheManager;
+import org.apache.jmeter.protocol.http.control.StaticHost;
 import org.apache.jmeter.testelement.TestElement;
 import org.apache.jmeter.testelement.property.JMeterProperty;
 import org.apache.jmeter.util.JMeterUtils;
 import org.apache.jorphan.gui.GuiUtils;
 import org.apache.jorphan.gui.layout.VerticalLayout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * This gui part of @see
@@ -55,18 +58,29 @@ import org.apache.jorphan.gui.layout.Ver
  */
 public class DNSCachePanel extends AbstractConfigGui implements ActionListener {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(DNSCacheManager.class);
+
     private static final long serialVersionUID = 2120L;
 
     public static final String OPTIONS = JMeterUtils.getResString("option");
 
     private static final String ADD_COMMAND = JMeterUtils.getResString("add"); // $NON-NLS-1$
 
+    private static final String ADD_HOST_COMMAND = JMeterUtils.getResString("add_host"); // $NON-NLS-1$
+
     private static final String DELETE_COMMAND = JMeterUtils.getResString("delete"); // $NON-NLS-1$
 
+    private static final String DELETE_HOST_COMMAND = JMeterUtils.getResString("delete_host"); // $NON-NLS-1$
+
     private static final String SYS_RES_COMMAND = JMeterUtils.getResString("use_system_dns_resolver"); // $NON-NLS-1$
 
     private static final String CUST_RES_COMMAND = JMeterUtils.getResString("use_custom_dns_resolver"); // $NON-NLS-1$
 
+    private JTable dnsHostsTable;
+
+    private JPanel dnsHostsPanel;
+    private JPanel dnsHostsButPanel;
+
     private JTable dnsServersTable;
 
     private JPanel dnsServersPanel;
@@ -74,6 +88,7 @@ public class DNSCachePanel extends Abstr
     private JPanel dnsServButPanel;
 
     private PowerTableModel dnsServersTableModel;
+    private PowerTableModel dnsHostsTableModel;
 
     private JRadioButton sysResButton;
 
@@ -83,6 +98,9 @@ public class DNSCachePanel extends Abstr
 
     private JButton addButton;
 
+    private JButton addHostButton;
+    private JButton deleteHostButton;
+
     private ButtonGroup providerDNSradioGroup = new ButtonGroup();
 
     private static final String[] COLUMN_RESOURCE_NAMES = {
@@ -91,6 +109,9 @@ public class DNSCachePanel extends Abstr
     private static final Class<?>[] columnClasses = {
         String.class };
 
+    private static final String[] HOSTS_COLUMN_RESOURCE_NAMES = { "host", JMeterUtils.getResString("dns_hostname_or_ip") };
+    private static final Class<?>[] HOSTS_COLUMN_CLASSES = { String.class, String.class };
+
     private JCheckBox clearEachIteration;
 
     /**
@@ -121,6 +142,11 @@ public class DNSCachePanel extends Abstr
                 String server = (String) dnsServersTableModel.getRowData(i)[0];
                 dnsCacheManager.addServer(server);
             }
+            for (int i = 0; i < dnsHostsTableModel.getRowCount(); i++) {
+                String host = (String) dnsHostsTableModel.getRowData(i)[0];
+                String addresses = (String) dnsHostsTableModel.getRowData(i)[1];
+                dnsCacheManager.addHost(host, addresses);
+            }
             dnsCacheManager.setClearEachIteration(clearEachIteration.isSelected());
             if (providerDNSradioGroup.isSelected(custResButton.getModel())) {
                 dnsCacheManager.setCustomResolver(true);
@@ -140,6 +166,8 @@ public class DNSCachePanel extends Abstr
         providerDNSradioGroup.setSelected(sysResButton.getModel(), true);
         dnsServersTableModel.clearData();
         deleteButton.setEnabled(false);
+        dnsHostsTableModel.clearData();
+        deleteHostButton.setEnabled(false);
 
     }
 
@@ -150,6 +178,13 @@ public class DNSCachePanel extends Abstr
         }
     }
 
+    private void populateHostsTable(DNSCacheManager resolver) {
+        dnsHostsTableModel.clearData();
+        for (JMeterProperty hostEntry : resolver.getHosts()) {
+            addHostToTable((StaticHost) hostEntry.getObjectValue());
+        }
+    }
+
     @Override
     public TestElement createTestElement() {
         DNSCacheManager dnsCacheManager = new DNSCacheManager();
@@ -163,11 +198,14 @@ public class DNSCachePanel extends Abstr
 
         DNSCacheManager dnsCacheManager = (DNSCacheManager) el;
         populateTable(dnsCacheManager);
+        populateHostsTable(dnsCacheManager);
         clearEachIteration.setSelected(dnsCacheManager.isClearEachIteration());
         if (dnsCacheManager.isCustomResolver()) {
             providerDNSradioGroup.setSelected(custResButton.getModel(), true);
             deleteButton.setEnabled(dnsServersTable.getColumnCount() > 0);
+            deleteHostButton.setEnabled(dnsHostsTable.getColumnCount() > 0);
             addButton.setEnabled(true);
+            addHostButton.setEnabled(true);
         } else {
             providerDNSradioGroup.setSelected(sysResButton.getModel(), true);
         }
@@ -175,6 +213,7 @@ public class DNSCachePanel extends Abstr
 
     private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
         dnsServersTableModel = new PowerTableModel(COLUMN_RESOURCE_NAMES, columnClasses);
+        dnsHostsTableModel = new PowerTableModel(HOSTS_COLUMN_RESOURCE_NAMES, HOSTS_COLUMN_CLASSES);
 
         clearEachIteration = new JCheckBox(JMeterUtils.getResString("clear_cache_each_iteration"), true); //$NON-NLS-1$
         setLayout(new BorderLayout());
@@ -190,8 +229,14 @@ public class DNSCachePanel extends Abstr
         northPanel.add(optionsPane);
         add(northPanel, BorderLayout.NORTH);
 
+        JPanel tables = new JPanel();
+        tables.setLayout(new VerticalLayout(2, VerticalLayout.BOTH));
         dnsServersPanel = createDnsServersTablePanel();
-        add(dnsServersPanel, BorderLayout.CENTER);
+        dnsHostsPanel = createDnsHostsTablePanel();
+        tables.add(dnsServersPanel);
+        tables.add(dnsHostsPanel);
+        add(tables, BorderLayout.CENTER);
+
 
     }
 
@@ -212,6 +257,23 @@ public class DNSCachePanel extends Abstr
         return panel;
     }
 
+    public JPanel createDnsHostsTablePanel() {
+        // create the JTable that holds header per row
+        dnsHostsTable = new JTable(dnsHostsTableModel);
+        JMeterUtils.applyHiDPI(dnsHostsTable);
+        dnsHostsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        dnsHostsTable.setPreferredScrollableViewportSize(new Dimension(400, 100));
+
+        JPanel panel = new JPanel(new BorderLayout(0, 5));
+        panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
+                JMeterUtils.getResString("dns_hosts"))); // $NON-NLS-1$
+        JScrollPane dnsHostsScrollPane = new JScrollPane(dnsHostsTable);
+        panel.add(dnsHostsScrollPane, BorderLayout.CENTER);
+        dnsHostsButPanel = createHostsButtonPanel();
+        panel.add(dnsHostsButPanel, BorderLayout.SOUTH);
+        return panel;
+    }
+
     private JPanel createChooseResPanel() {
         JPanel chooseResPanel = new JPanel(new BorderLayout(0, 5));
         sysResButton = new JRadioButton();
@@ -248,6 +310,18 @@ public class DNSCachePanel extends Abstr
         return buttonPanel;
     }
 
+    private JPanel createHostsButtonPanel() {
+        boolean tableEmpty = dnsHostsTableModel.getRowCount() == 0;
+
+        addHostButton = createButton("add_host", 'H', ADD_HOST_COMMAND, custResButton.isSelected()); // $NON-NLS-1$
+        deleteHostButton = createButton("delete_host", 'X', DELETE_HOST_COMMAND, !tableEmpty); // $NON-NLS-1$
+
+        JPanel buttonPanel = new JPanel();
+        buttonPanel.add(addHostButton, BorderLayout.WEST);
+        buttonPanel.add(deleteHostButton, BorderLayout.LINE_END);
+        return buttonPanel;
+    }
+
     private JButton createButton(String resName, char mnemonic, String command, boolean enabled) {
         JButton button = new JButton(JMeterUtils.getResString(resName));
         button.setMnemonic(mnemonic);
@@ -262,63 +336,85 @@ public class DNSCachePanel extends Abstr
             dnsServer });
     }
 
+    private void addHostToTable(StaticHost hostEntry) {
+        LOGGER.debug("Adding entry {}", hostEntry);
+        dnsHostsTableModel.addRow(new Object[] {
+            hostEntry.getName(), hostEntry.getAddress() });
+    }
+
     @Override
     public void actionPerformed(ActionEvent e) {
         String action = e.getActionCommand();
-        dnsServersTable.setEnabled(custResButton.isSelected());
+
+        enableTable(custResButton.isSelected(), sysResButton.isSelected(), dnsServersTable, dnsServersTableModel,
+                addButton, deleteButton);
+        enableTable(custResButton.isSelected(), sysResButton.isSelected(), dnsHostsTable, dnsHostsTableModel,
+                addHostButton, deleteHostButton);
+
+        if (action.equals(DELETE_COMMAND)) {
+            deleteTableRow(dnsServersTable, dnsServersTableModel, deleteButton);
+        } else if (action.equals(ADD_COMMAND)) {
+            addTableRow(dnsServersTable, dnsServersTableModel, deleteButton);
+        } else if (DELETE_HOST_COMMAND.equals(action)) {
+            deleteTableRow(dnsHostsTable, dnsHostsTableModel, deleteHostButton);
+        } else if (ADD_HOST_COMMAND.equals(action)) {
+            addTableRow(dnsHostsTable, dnsHostsTableModel, deleteHostButton);
+        }
+    }
+
+    private void enableTable(boolean custEnabled, boolean sysEnabled, JTable table, PowerTableModel model,
+            JButton addButton, JButton deleteButton) {
+        table.setEnabled(custEnabled);
         Color greyColor = new Color(240, 240, 240);
         Color blueColor = new Color(184, 207, 229);
-        dnsServersTable.setBackground(sysResButton.isSelected() ? greyColor : Color.WHITE);
-        dnsServersTable.setSelectionBackground(sysResButton.isSelected() ? greyColor : blueColor);
-        addButton.setEnabled(custResButton.isSelected());
-        deleteButton.setEnabled(custResButton.isSelected());
-        if (custResButton.isSelected() && (dnsServersTableModel.getRowCount() > 0)) {
+        table.setBackground(sysEnabled ? greyColor : Color.WHITE);
+        table.setSelectionBackground(sysEnabled ? greyColor : blueColor);
+        addButton.setEnabled(custEnabled);
+        deleteButton.setEnabled(custEnabled);
+        if (custEnabled && (model.getRowCount() > 0)) {
             deleteButton.setEnabled(true);
             addButton.setEnabled(true);
         }
+    }
 
-        if (action.equals(DELETE_COMMAND)) {
-            if (dnsServersTableModel.getRowCount() > 0) {
-                // If a table cell is being edited, we must cancel the editing
-                // before deleting the row.
-                GuiUtils.cancelEditing(dnsServersTable);
-
-                int rowSelected = dnsServersTable.getSelectedRow();
-
-                if (rowSelected != -1) {
-                    dnsServersTableModel.removeRow(rowSelected);
-                    dnsServersTableModel.fireTableDataChanged();
-
-                    if (dnsServersTableModel.getRowCount() == 0) {
-                        deleteButton.setEnabled(false);
-                    }
-
-                    else {
-                        int rowToSelect = rowSelected;
-
-                        if (rowSelected >= dnsServersTableModel.getRowCount()) {
-                            rowToSelect = rowSelected - 1;
-                        }
+    private void addTableRow(JTable table, PowerTableModel model, JButton button) {
+        // If a table cell is being edited, we should accept the current
+        // value and stop the editing before adding a new row.
+        GuiUtils.stopTableEditing(table);
 
-                        dnsServersTable.setRowSelectionInterval(rowToSelect, rowToSelect);
-                    }
-                }
-            }
-        } else if (action.equals(ADD_COMMAND)) {
-            // If a table cell is being edited, we should accept the current
-            // value and stop the editing before adding a new row.
-            GuiUtils.stopTableEditing(dnsServersTable);
+        model.addNewRow();
+        model.fireTableDataChanged();
+
+        if (!button.isEnabled()) {
+            button.setEnabled(true);
+        }
 
-            dnsServersTableModel.addNewRow();
-            dnsServersTableModel.fireTableDataChanged();
+        // Highlight (select) the appropriate row.
+        int rowToSelect = model.getRowCount() - 1;
+        table.setRowSelectionInterval(rowToSelect, rowToSelect);
+    }
 
-            if (!deleteButton.isEnabled()) {
-                deleteButton.setEnabled(true);
-            }
+    private void deleteTableRow(JTable table, PowerTableModel model, JButton button) {
+        if (model.getRowCount() > 0) {
+            // If a table cell is being edited, we must cancel the editing
+            // before deleting the row.
+            GuiUtils.cancelEditing(table);
 
-            // Highlight (select) the appropriate row.
-            int rowToSelect = dnsServersTableModel.getRowCount() - 1;
-            dnsServersTable.setRowSelectionInterval(rowToSelect, rowToSelect);
+            int rowSelected = table.getSelectedRow();
+
+            if (rowSelected != -1) {
+                model.removeRow(rowSelected);
+                model.fireTableDataChanged();
+
+                if (model.getRowCount() == 0) {
+                    button.setEnabled(false);
+                }
+
+                else {
+                    int rowToSelect = Math.min(rowSelected, model.getRowCount() - 1);
+                    table.setRowSelectionInterval(rowToSelect, rowToSelect);
+                }
+            }
         }
     }
 }

Modified: jmeter/trunk/test/src/org/apache/jmeter/protocol/http/control/TestDNSCacheManager.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/test/src/org/apache/jmeter/protocol/http/control/TestDNSCacheManager.java?rev=1782622&r1=1782621&r2=1782622&view=diff
==============================================================================
--- jmeter/trunk/test/src/org/apache/jmeter/protocol/http/control/TestDNSCacheManager.java (original)
+++ jmeter/trunk/test/src/org/apache/jmeter/protocol/http/control/TestDNSCacheManager.java Sat Feb 11 17:04:06 2017
@@ -18,12 +18,15 @@
 
 package org.apache.jmeter.protocol.http.control;
 
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.Arrays;
 
 import org.apache.jmeter.junit.JMeterTestCase;
+import org.hamcrest.CoreMatchers;
 import org.junit.Assert;
 import org.junit.Test;
 import org.xbill.DNS.ExtendedResolver;
@@ -32,6 +35,39 @@ public class TestDNSCacheManager extends
     private static final String INVALID_DNS_SERVER = "8.8.8.8.9"; //$NON-NLS-1$
     
     private static final String VALID_DNS_SERVER = "8.8.8.8"; //$NON-NLS-1$
+
+    @Test
+    public void testWithOneStaticHost() throws Exception {
+        DNSCacheManager manager = new DNSCacheManager();
+        manager.setCustomResolver(true);
+        manager.addHost("jmeter.example.org", "127.0.0.1");
+        assertThat(manager.resolve("jmeter.example.org"),
+                CoreMatchers.is(CoreMatchers.equalTo(new InetAddress[] { InetAddress.getByName("127.0.0.1") })));
+    }
+
+    @Test
+    public void testWithMultipleStaticHost() throws Exception {
+        DNSCacheManager manager = new DNSCacheManager();
+        manager.setCustomResolver(true);
+        manager.addHost("jmeter.example.org", "127.0.0.1, 1.2.3.4");
+        assertThat(manager.resolve("jmeter.example.org"),
+                CoreMatchers.is(CoreMatchers.equalTo(new InetAddress[] { InetAddress.getByName("127.0.0.1"), InetAddress.getByName("1.2.3.4") })));
+    }
+
+    @Test
+    public void testAddAndClearStaticHost() throws Exception {
+        DNSCacheManager manager = new DNSCacheManager();
+        manager.setCustomResolver(true);
+        manager.addHost("apache.jmeter.org", "127.0.0.1");
+        manager.resolve("apache.jmeter.org");
+        manager.clear();
+        assertThat(Arrays.asList(manager.resolve("jmeter.apache.org")),
+                CoreMatchers.hasItem(InetAddress.getByName("jmeter.apache.org")));
+        assertThat(Arrays.asList(manager.resolve("jmeter.apache.org")),
+                CoreMatchers.not(CoreMatchers.hasItem(InetAddress.getByName("127.0.0.1"))));
+    }
+
+    
     @Test
     public void testWithCustomResolverAnd1WrongServer() throws UnknownHostException {
         DNSCacheManager original = new DNSCacheManager();

Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1782622&r1=1782621&r2=1782622&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml [utf-8] (original)
+++ jmeter/trunk/xdocs/changes.xml [utf-8] Sat Feb 11 17:04:06 2017
@@ -164,6 +164,7 @@ JMeter now requires Java 8. Ensure you u
     <li><bug>60602</bug>XPath Extractor : Add Match No. to allow extraction randomly, by index or all matches</li>
     <li><bug>60710</bug>XPath Extractor : When content on which assertion applies is not XML, in View Results Tree the extractor is marked in Red and named SAXParseException. Contributed by Ubik Load Pack (support at ubikloadpack.com)</li>
     <li><bug>60712</bug>Response Assertion : Improve Renderer of Patterns</li>
+    <li><bug>59174</bug>Add a table with static hosts to the DNS Cache Manager. This enables better virtual hosts testing with HttpClient4.</li>
 </ul>
 
 <h3>Functions</h3>

Modified: jmeter/trunk/xdocs/images/screenshots/dns-cache-manager.png
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/images/screenshots/dns-cache-manager.png?rev=1782622&r1=1782621&r2=1782622&view=diff
==============================================================================
Binary files - no diff available.

Modified: jmeter/trunk/xdocs/usermanual/component_reference.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/usermanual/component_reference.xml?rev=1782622&r1=1782621&r2=1782622&view=diff
==============================================================================
--- jmeter/trunk/xdocs/usermanual/component_reference.xml (original)
+++ jmeter/trunk/xdocs/usermanual/component_reference.xml Sat Feb 11 17:04:06 2017
@@ -3640,11 +3640,15 @@ By default, a Graphite implementation is
 <component name="DNS Cache Manager" index="&sect-num;.4.3"  width="712" height="387" screenshot="dns-cache-manager.png">
     <note>DNS Cache Manager is designed for using in the root of Thread Group or Test Plan. Do not place it as child element of particular HTTP Sampler
     </note>
-    <note>DNS Cache Manager working only with the HTTP request using HTTPClient4 implementation.</note>
+    <note>DNS Cache Manager works only with HTTP requests using HTTPClient4 implementation.</note>
     <description><p>The DNS Cache Manager element allows to test applications, which have several servers behind load balancers (CDN, etc.), 
     when user receives content from different IP's. By default JMeter uses JVM DNS cache. That's why
-    only one server from the cluster receives load. DNS Cache Manager resolves name for each thread separately each iteration and
-    saves results of resolving to its internal DNS Cache, which independent from both JVM and OS DNS caches.
+    only one server from the cluster receives load. DNS Cache Manager resolves names for each thread separately each iteration and
+    saves results of resolving to its internal DNS Cache, which is independent from both JVM and OS DNS caches.
+    </p>
+    <p>
+    A mapping for static hosts can be used to simulate something like <code>/etc/hosts</code> file.
+    These entries will be preferred over the custom resolver.
     </p>
     </description>
     <properties>
@@ -3653,10 +3657,13 @@ By default, a Graphite implementation is
     <property name="Use system DNS resolver" required="N/A">System DNS resolver will be used. For correct work edit
        <code>$JAVA_HOME/jre/lib/security/java.security</code> and add <code>networkaddress.cache.ttl=0</code> 
     </property>
-    <property name="Use custom DNS resolver" required="N/A">Custom DNS resolver(from dnsjava library) will be used.</property>
+    <property name="Use custom DNS resolver" required="N/A">Custom DNS resolver (from dnsjava library) will be used.</property>
     <property name="Hostname or IP address" required="No">List of DNS servers to use. If empty, network configuration DNS will used.</property>
     <property name="Add Button" required="N/A">Add an entry to the DNS servers table.</property>
     <property name="Delete Button" required="N/A">Delete the currently selected table entry.</property>
+    <property name="Host and Hostname or IP address" required="No">Mapping of hostnames to a static host entry which will be resolved using the custom DNS resolver.</property>
+    <property name="Add static host Button" required="N/A">Add an entry to the static hosts table.</property>
+    <property name="Delete static host Button" required="N/A">Delete the currently selected static host in the table.</property>
     </properties>
 </component>