Author: pmouawad
Date: Sun Jan 29 20:35:59 2017
New Revision: 1780852

URL: http://svn.apache.org/viewvc?rev=1780852&view=rev
Log:
Avoid NPE if init of resolver fails
Add Test cases

Modified:
    
jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/control/DNSCacheManager.java
    
jmeter/trunk/test/src/org/apache/jmeter/protocol/http/control/TestDNSCacheManager.java

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=1780852&r1=1780851&r2=1780852&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
 Sun Jan 29 20:35:59 2017
@@ -61,34 +61,36 @@ public class DNSCacheManager extends Con
 
     private static final Logger log = LoggingManager.getLoggerForClass();
 
-    private transient SystemDefaultDnsResolver systemDefaultDnsResolver = null;
-
-    private Map<String, InetAddress[]> cache = null;
-
-    private transient Resolver resolver = null;
+    public static final boolean DEFAULT_CLEAR_CACHE_EACH_ITER = false;
 
     //++ JMX tag values
-    public static final String CLEAR_CACHE_EACH_ITER = 
"DNSCacheManager.clearEachIteration"; // $NON-NLS-1$
+    private static final String CLEAR_CACHE_EACH_ITER = 
"DNSCacheManager.clearEachIteration"; // $NON-NLS-1$
 
-    public static final String SERVERS = "DNSCacheManager.servers"; // 
$NON-NLS-1$
+    private static final String SERVERS = "DNSCacheManager.servers"; // 
$NON-NLS-1$
 
-    public static final String IS_CUSTOM_RESOLVER = 
"DNSCacheManager.isCustomResolver"; // $NON-NLS-1$
+    private static final String IS_CUSTOM_RESOLVER = 
"DNSCacheManager.isCustomResolver"; // $NON-NLS-1$
     //-- JMX tag values
 
-    public static final boolean DEFAULT_CLEAR_CACHE_EACH_ITER = false;
+    private static final boolean DEFAULT_IS_CUSTOM_RESOLVER = false;
 
-    public static final String DEFAULT_SERVERS = ""; // $NON-NLS-1$
+    private final transient Cache lookupCache;
 
-    public static final boolean DEFAULT_IS_CUSTOM_RESOLVER = false;
+    private final transient SystemDefaultDnsResolver systemDefaultDnsResolver;
 
-    private final transient Cache lookupCache;
+    private final Map<String, InetAddress[]> cache;
+
+    transient Resolver resolver;
 
     private transient int timeoutMs;
 
+    transient boolean initFailed;
+
     // ensure that the initial DNSServers are copied to the per-thread 
instances
 
     public DNSCacheManager() {
         setProperty(new CollectionProperty(SERVERS, new ArrayList<String>()));
+        this.systemDefaultDnsResolver = new SystemDefaultDnsResolver();
+        this.cache = new LinkedHashMap<>();
         //disabling cache
         lookupCache = new Cache();
         lookupCache.setMaxCache(0);
@@ -101,8 +103,14 @@ public class DNSCacheManager extends Con
     @Override
     public Object clone() {
         DNSCacheManager clone = (DNSCacheManager) super.clone();
-        clone.systemDefaultDnsResolver = new SystemDefaultDnsResolver();
-        clone.cache = new LinkedHashMap<>();
+        clone.resolver = createResolver();
+        return clone;
+    }
+
+    /**
+     * @return {@link Resolver}
+     */
+    private Resolver createResolver() {
         CollectionProperty dnsServers = getServers();
         try {
             String[] serverNames = new String[dnsServers.size()];
@@ -111,16 +119,20 @@ public class DNSCacheManager extends Con
                 serverNames[index] = jMeterProperty.getStringValue();
                 index++;
             }
-            clone.resolver = new ExtendedResolver(serverNames);
-            log.debug("Using DNS Resolvers: "
-                    + Arrays.asList(((ExtendedResolver) clone.resolver)
-                            .getResolvers()));
+            ExtendedResolver resolver = new ExtendedResolver(serverNames);
+            if(log.isDebugEnabled()) {
+                log.debug("Using DNS Resolvers: "
+                        + Arrays.asList(((ExtendedResolver) resolver)
+                                .getResolvers()));
+            }
             // resolvers will be chosen via round-robin
-            ((ExtendedResolver) clone.resolver).setLoadBalance(true);
+            resolver.setLoadBalance(true);
+            return resolver;
         } catch (UnknownHostException uhe) {
+            this.initFailed = true;
             log.warn("Failed to create Extended resolver: " + 
uhe.getMessage());
+            return null;
         }
-        return clone;
     }
 
     /**
@@ -129,12 +141,19 @@ public class DNSCacheManager extends Con
      */
     @Override
     public InetAddress[] resolve(String host) throws UnknownHostException {
-        if (cache.containsKey(host)) {
+        InetAddress[] result = cache.get(host);
+        // cache may contain
+        // 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 
+        // explicitly maps the key to null
+        // 
https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashMap.html
+        if (result != null || cache.containsKey(host)) {
             if (log.isDebugEnabled()) {
                 log.debug("Cache hit thr#" + 
JMeterContextService.getContext().getThreadNum() + ": " + host + "=>"
-                        + Arrays.toString(cache.get(host)));
+                        + Arrays.toString(result));
             }
-            return cache.get(host);
+            return result;
         } else {
             InetAddress[] addresses = requestLookup(host);
             if (log.isDebugEnabled()) {
@@ -148,39 +167,60 @@ public class DNSCacheManager extends Con
 
     /**
      * Sends DNS request via system or custom DNS resolver
+     * @param host
+     * @return array of {@link InetAddress} or null if lookup did not return 
result
      */
     private InetAddress[] requestLookup(String host) throws 
UnknownHostException {
         InetAddress[] addresses = null;
-        if (isCustomResolver() && ((ExtendedResolver) 
resolver).getResolvers().length > 0) {
-            try {
-                Lookup lookup = new Lookup(host, Type.A);
-                lookup.setCache(lookupCache);
-                if (timeoutMs > 0) {
-                    resolver.setTimeout(timeoutMs / 1000, timeoutMs % 1000);
-                }
-                lookup.setResolver(resolver);
-                Record[] records = lookup.run();
-                if (records == null || records.length == 0) {
-                    throw new UnknownHostException("Failed to resolve host 
name: " + host);
-                }
-                addresses = new InetAddress[records.length];
-                for (int i = 0; i < records.length; i++) {
-                    addresses[i] = ((ARecord) records[i]).getAddress();
+        if (isCustomResolver()) {
+            if (getResolver() != null) {
+                if(getResolver().getResolvers().length > 0) {
+                    try {
+                        Lookup lookup = new Lookup(host, Type.A);
+                        lookup.setCache(lookupCache);
+                        if (timeoutMs > 0) {
+                            resolver.setTimeout(timeoutMs / 1000, timeoutMs % 
1000);
+                        }
+                        lookup.setResolver(resolver);
+                        Record[] records = lookup.run();
+                        if (records == null || records.length == 0) {
+                            throw new UnknownHostException("Failed to resolve 
host name: " + host);
+                        }
+                        addresses = new InetAddress[records.length];
+                        for (int i = 0; i < records.length; i++) {
+                            addresses[i] = ((ARecord) records[i]).getAddress();
+                        }
+                    } catch (TextParseException tpe) {
+                        log.debug("Failed to create Lookup object: " + tpe);
+                    }
+                    return addresses;
                 }
-            } catch (TextParseException tpe) {
-                log.debug("Failed to create Lookup object: " + tpe);
-            }
-        } else {
-            addresses = systemDefaultDnsResolver.resolve(host);
-            if (log.isDebugEnabled()) {
-                log.debug("Cache miss: " + host + " Thread #" + 
JMeterContextService.getContext().getThreadNum()
-                        + ", resolved with system resolver into " + 
Arrays.toString(addresses));
+            } else {
+                throw new UnknownHostException("Could not resolve host:"+host
+                        +", failed to initialize resolver"
+                        + " or no resolver found");
             }
         }
+        addresses = systemDefaultDnsResolver.resolve(host);
+        if (log.isDebugEnabled()) {
+            log.debug("Cache miss: " + host + " Thread #" + 
JMeterContextService.getContext().getThreadNum()
+                    + ", resolved with system resolver into " + 
Arrays.toString(addresses));
+        }
         return addresses;
     }
 
     /**
+     * Tries to initialize resolver , otherwise sets initFailed to true
+     * @return ExtendedResolver if init succeeded or null otherwise
+     */
+    private ExtendedResolver getResolver() {
+        if(resolver == null && !initFailed) {
+            resolver = createResolver();
+        }
+        return (ExtendedResolver) resolver;
+    }
+
+    /**
      * {@inheritDoc} Clean DNS cache if appropriate check-box was selected
      */
     @Override
@@ -197,6 +237,9 @@ public class DNSCacheManager extends Con
     public void clear() {
         super.clear();
         clearServers(); // ensure data is set up OK initially
+        this.cache.clear();
+        this.initFailed = false;
+        this.resolver = null;
     }
 
     /**

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=1780852&r1=1780851&r2=1780852&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
 Sun Jan 29 20:35:59 2017
@@ -20,18 +20,92 @@ package org.apache.jmeter.protocol.http.
 
 import static org.junit.Assert.fail;
 
+import java.net.InetAddress;
 import java.net.UnknownHostException;
 
 import org.apache.jmeter.junit.JMeterTestCase;
+import org.junit.Assert;
 import org.junit.Test;
+import org.xbill.DNS.ExtendedResolver;
 
 public class TestDNSCacheManager extends JMeterTestCase {
-
+    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 testWithCustomResolverAnd1WrongServer() throws 
UnknownHostException {
+        DNSCacheManager original = new DNSCacheManager();
+        original.addServer(INVALID_DNS_SERVER);
+        original.setCustomResolver(true);
+        original.setTimeoutMs(100);
+        try {
+            original.resolve("jmeter.apache.org");
+            fail("Should have failed as DNS server does not exist");
+        } catch (UnknownHostException e) {
+            Assert.assertNull(original.resolver);
+            Assert.assertTrue(original.initFailed);
+        }
+        
+        try {
+            original.resolve("www.apache.org");
+            fail("Should have failed as DNS server does not exist");
+            // OK
+        } catch (UnknownHostException e) {
+            Assert.assertNull(original.resolver);
+            Assert.assertTrue(original.initFailed);
+        }
+    }
+    
+    @Test
+    public void testWithCustomResolverAnd1Server() throws UnknownHostException 
{
+        DNSCacheManager original = new DNSCacheManager();
+        original.addServer(VALID_DNS_SERVER);
+        original.setCustomResolver(true);
+        original.setTimeoutMs(100);
+        try {
+            original.resolve("jmeter.apache.org");
+            Assert.assertNotNull(original.resolver);
+            
Assert.assertTrue(((ExtendedResolver)original.resolver).getResolvers().length==1);
+            // OK
+        } catch (UnknownHostException e) {
+            fail("System DNS server should have been used");
+        }
+    }
+    
+    @Test
+    public void testWithCustomResolverAndNoServer() throws 
UnknownHostException {
+        DNSCacheManager original = new DNSCacheManager();
+        original.setCustomResolver(true);
+        original.setTimeoutMs(100);
+        try {
+            // This will use Default System DNS resolver
+            original.resolve("jmeter.apache.org");
+            Assert.assertNotNull(original.resolver);
+            
Assert.assertTrue(((ExtendedResolver)original.resolver).getResolvers().length==0);
+        } catch (UnknownHostException e) {
+            fail("Should have failed as no DNS server provided");
+        }
+    }
+    
+    @Test
+    public void testWithCustomResolverAndInvalidNameserver() throws 
UnknownHostException {
+        DNSCacheManager original = new DNSCacheManager();
+        original.setCustomResolver(true);
+        original.addServer(INVALID_DNS_SERVER);
+        original.setTimeoutMs(100);
+        try {
+            original.resolve("jmeter.apache.org");
+            fail();
+        } catch (UnknownHostException e) {
+            // OK
+        }
+    }
+    
     @Test
     public void testCloneWithCustomResolverAndInvalidNameserver() throws 
UnknownHostException {
         DNSCacheManager original = new DNSCacheManager();
         original.setCustomResolver(true);
-        original.addServer("127.0.0.99");
+        original.addServer(INVALID_DNS_SERVER);
         DNSCacheManager clone = (DNSCacheManager) original.clone();
         clone.setTimeoutMs(100);
         try {
@@ -41,5 +115,32 @@ public class TestDNSCacheManager extends
             // OK
         }
     }
-
+    
+    @Test
+    public void testResolveExistingHostWithSystemDefaultDnsServer() throws 
UnknownHostException {
+        DNSCacheManager original = new DNSCacheManager();
+        original.setCustomResolver(false);
+        try {
+            InetAddress[] result = original.resolve("www.example.org");
+            Assert.assertNotNull(result);
+            Assert.assertNull(original.resolver);
+            // IPv4 and IPv6
+            Assert.assertTrue(result.length == 2);
+        } catch (UnknownHostException e) {
+            Assert.fail("Should not have failed");
+        }
+    }
+    
+    @Test
+    public void testResolveNonExistingHostWithSystemDefaultDnsServer() throws 
UnknownHostException {
+        DNSCacheManager original = new DNSCacheManager();
+        original.setCustomResolver(false);
+        try {
+            original.resolve("jmeterxxx.apache.org");
+            fail();
+        } catch (UnknownHostException e) {
+            Assert.assertNull(original.resolver);
+            // OK
+        }
+    }
 }


Reply via email to