United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4692867 : UnKnowHostException still occurs after network connection recovers

Details
Type:
Bug
Submit Date:
2002-05-29
Status:
Resolved
Updated Date:
2003-03-13
Project Name:
JDK
Resolved Date:
2002-09-26
Component:
core-libs
OS:
generic,windows_2000
Sub-Component:
java.net
CPU:
x86,generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.4.1
Fixed Versions:
1.4.1_03 (03)

Related Reports
Backport:
Duplicate:
Relates:

Sub Tasks

Description
While an application is working, the network connection is terminated.
Then, even though the network connection recover from its termination.
the InetAddress.getByName() can not get the IP address and outputs "UnKnownHostException" 

Expectation is that the getByName() can get IP address after the network connection's recovery.


1. Reproducing

 1) Modify 2 lines of the attached sample code.

    Please specify host name of each which are connectted to the network.
  
    ex.
      String hostA = "srv1";
      String hostB = "srv2";

 2) Compile  the source code
 3) Invoke "Java InetAddressTP"
    -> You will see the following message.
   
** 1st resolution must be resolved **
srv1/111.222.33.44
** Thread will sleep, disable network by detaching the network cable **

 4) Disable the network connection
    ex. 
      by pulling out LAN connector

 5) Wait about 20 seconds, you will see the following.

java.net.UnknownHostException: srv2
        at java.net.InetAddress.getAllByName0(InetAddress.java:948)
        at java.net.InetAddress.getAllByName0(InetAddress.java:918)
        at java.net.InetAddress.getAllByName(InetAddress.java:912)
        at java.net.InetAddress.getByName(InetAddress.java:832)
        at InetAddressTP.main(InetAddressTP.java:36)
** Thread will sleep, enable network by connecting the network cable **

  6) Enable the netwrok connection
      ex. 
        by inserting  LAN connector
 
  7) You will see the following message

** 3rd resolution, must be resolved **
java.net.UnknownHostException: srv2
        at java.net.InetAddress.getAllByName0(InetAddress.java:953)
        at java.net.InetAddress.getAllByName0(InetAddress.java:918)
        at java.net.InetAddress.getAllByName(InetAddress.java:912)
        at java.net.InetAddress.getByName(InetAddress.java:832)
        at InetAddressTP.main(InetAddressTP.java:56)

  Here, we expect the following message shows up because the network connection
  is recovered and alive, 

** 3rd resolution, must be resolved **
srv2/111.222.33.55

but the above exception appears.


2. Configration
  
 MPU : Pentium IV 1.4 [GHz]
 Mem : 384 [MB]
 OS  : Windows2000 (SP2, Japanese)
 JDK : 1.4.1-beta-b13, 1.4.0fcs, 1.3.1_03


3. Note

 - Please imagine the case that network connection is terminated and 
   recover automatically.
   Besides, your application runs like server process and try to connect
   some nodes periodically.
   If your application is running on a server, you expect the application
   can reconnect automatically when the network connection recovers ?

   However, according to the above-mentioned behavior,
   once the network connection is terminated, the application 
   can not reconnect later on automatically even if it recovers.

   That looks big problem for the application on a server.


2002-05-29
===============================================================================

                                    

Comments
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
1.4.1_03
mantis

FIXED IN:
1.4.1_03
mantis

INTEGRATED IN:
1.4.1_03
mantis
mantis-b03


                                     
2004-06-14
SUGGESTED FIX

------- InetAddress.java -------
*** /tmp/geta8727       Thu Sep 19 14:25:32 2002
--- /tmp/getb8727       Thu Sep 19 14:25:32 2002
***************
*** 1,5 ****
  /*
!  * %W% %E%
   *
   * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
   * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
--- 1,5 ----
  /*
!  * %W% %E% 
   *
   * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
   * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
***************
*** 11,17 ****
  import java.util.LinkedHashMap;
  import java.util.Random;
  import java.util.Iterator;
! import java.util.Vector;
  import java.security.AccessController;
  import java.io.ObjectStreamException;
  import sun.security.action.*;
--- 11,17 ----
  import java.util.LinkedHashMap;
  import java.util.Random;
  import java.util.Iterator;
! import java.util.LinkedList;
  import java.security.AccessController;
  import java.io.ObjectStreamException;
  import sun.security.action.*;
***************
*** 569,576 ****
      /*
       * Cached addresses - our own litle nis, not!
       */
!     private static LinkedHashMap          addressCache = new LinkedHashMap();
!     private static boolean                addressCacheInit = false;
      static InetAddress[]    unknown_array; // put THIS in cache
  
      static InetAddressImpl  impl;
--- 569,582 ----
      /*
       * Cached addresses - our own litle nis, not!
       */
!     private static Cache addressCache =
!         new Cache(InetAddressCachePolicy.get());
! 
!     private static Cache negativeCache =
!         new Cache(InetAddressCachePolicy.getNegative());
! 
!     private static boolean addressCacheInit = false;
! 
      static InetAddress[]    unknown_array; // put THIS in cache
  
      static InetAddressImpl  impl;
***************
*** 577,700 ****
  
      private static HashMap          lookupTable = new HashMap();
  
      static final class CacheEntry {
  
!       CacheEntry(String hostname, Object address, long expiration) {
!           this.hostname = hostname;
!           this.address = address;
!           this.expiration = expiration;
!       }
  
!       String hostname;
!       Object address;
!       long expiration;
      }
  
!     /*
!      * Initialize cache and insert anyLocalAddress into the
!      * unknown array with no expiry.
       */
!     private static void cacheInitIfNeeded() {
!       synchronized (addressCache) {
!           if (addressCacheInit) {
!               return;
!           }
!           unknown_array = new InetAddress[1];
!           unknown_array[0] = impl.anyLocalAddress();
  
!           String hostname = impl.anyLocalAddress().getHostName();
  
!           CacheEntry entry = new CacheEntry(hostname,
!                                             unknown_array, 
!                                             InetAddressCachePolicy.FOREVER);
!           addressCache.put(hostname, entry);
  
!           addressCacheInit = true;
!       }
!     }
  
!     private static void cacheAddress(String hostname, Object address,
!                                    boolean success) {
  
!       int policy = (success ? 
!                     InetAddressCachePolicy.get() :
!                     InetAddressCachePolicy.getNegative());
  
!       // if the cache policy is to cache nothing, just return
  
!       if (policy == 0) {
!           return;
        }
  
!       long expiration = -1;
!       if (policy != InetAddressCachePolicy.FOREVER) {
!           expiration = System.currentTimeMillis() + (policy * 1000);
        }
-       cacheAddress(hostname, address, expiration);
      }
  
!     private static void cacheAddress(String hostname, Object address, long expiration) {
!         hostname = hostname.toLowerCase();
  
!       /* init cache on first call */
!       cacheInitIfNeeded();
  
        synchronized (addressCache) {
!           CacheEntry entry = (CacheEntry)addressCache.get(hostname);
!           if (entry == null) {
!               entry = new CacheEntry(hostname, address, expiration);
!               addressCache.put(hostname, entry);
            } else {
!               if (!entry.address.equals(address)) {
!                   entry.address = address;
!                   entry.expiration = expiration;
!                   // remove the old entry from the cache 
!                   // and add a new item
!                   addressCache.remove(hostname);
!                   addressCache.put(hostname, entry);
!               }
            }
        }
      }
  
      private static Object getCachedAddress(String hostname) {
          hostname = hostname.toLowerCase();
-       if ((InetAddressCachePolicy.get() == 0) && 
-           (InetAddressCachePolicy.getNegative() == 0)) {
-           return null;
-       }
  
!       /* init cache on first call */
!         cacheInitIfNeeded(); 
  
        synchronized (addressCache) {
!           // purge expired entries from cache
!           Iterator itr = addressCache.keySet().iterator();
!           // pass the first entry, which is for unknown address
!           itr.next();
!           Vector expired = new Vector();
!           while (itr.hasNext()) {
!               Object key = itr.next();
!               CacheEntry value = (CacheEntry)addressCache.get(key);
!               if (value != null && value.expiration < System.currentTimeMillis() &&
!                   value.expiration >= 0) {
!                   expired.add(key);
!               } else {
!                   break;
!               }
!           }
  
!           for (int i = 0; i < expired.size(); i++) {
!               Object key = expired.elementAt(i);
!               addressCache.remove(key);
!           }
  
!           CacheEntry entry = (CacheEntry)addressCache.get(hostname);
            if (entry == null) {
!               return null;
            }
!           return entry.address;
!       }
      }
  
      static {
--- 583,752 ----
  
      private static HashMap          lookupTable = new HashMap();
  
+     /**
+      * Represents a cache entry 
+      */
      static final class CacheEntry {
  
!         CacheEntry(Object address, long expiration) {
!             this.address = address;
!             this.expiration = expiration;
!         }
  
!         Object address;
!         long expiration;
      }
  
!     /**
!      * A cache that manages entries based on a policy specified
!      * at creation time.
       */
!     static final class Cache {
!       private int policy;
!       private LinkedHashMap cache; 
  
!       /**
!        * Create cache with specific policy 
!        */
!       public Cache(int policy) {
!           this.policy = policy;
!           cache = new LinkedHashMap();
!       }
  
!       /**
!        * Add an entry to the cache. If there's already an
!        * entry then for this host then the entry will be 
!        * replaced.
!        */
!       public Cache put(String host, Object address) {
!           if (policy == InetAddressCachePolicy.NEVER) {
!                 return this;
!           }
  
!           // purge any expired entries
  
!           if (policy != InetAddressCachePolicy.FOREVER) {
  
!               // As we iterate in insertion order we can
!               // terminate when a non-expired entry is found.
!                 LinkedList expired = new LinkedList();
!                 Iterator i = cache.keySet().iterator();
!               long now = System.currentTimeMillis();
!                 while (i.hasNext()) {
!                     String key = (String)i.next();
!                     CacheEntry entry = (CacheEntry)cache.get(key);
  
!                     if (entry.expiration >= 0 && entry.expiration < now) {
!                         expired.add(key);
!                     } else {
!                         break;
!                     }
!                 }
  
!                 i = expired.iterator();
!                 while (i.hasNext()) {
!                     cache.remove(i.next());
!               }
!             }
! 
!           // create new entry and add it to the cache
!           // -- as a HashMap replaces existing entries we
!           //    don't need to explicitly check if there is 
!           //    already an entry for this host.
!           long expiration;
!           if (policy == InetAddressCachePolicy.FOREVER) {
!               expiration = -1;
!           } else {
!               expiration = System.currentTimeMillis() + (policy * 1000);
!           }
!           CacheEntry entry = new CacheEntry(address, expiration);
!           cache.put(host, entry);
!           return this;
        }
  
!       /**
!        * Query the cache for the specific host. If found then
!        * return its CacheEntry, or null if not found.
!        */
!       public CacheEntry get(String host) {
!           if (policy == InetAddressCachePolicy.NEVER) {
!               return null;
!           }
!           CacheEntry entry = (CacheEntry)cache.get(host);
! 
!           // check if entry has expired
!           if (entry != null && policy != InetAddressCachePolicy.FOREVER) {
!               if (entry.expiration >= 0 && 
!                   entry.expiration < System.currentTimeMillis()) {
!                   cache.remove(host);
!                   entry = null;
!               }
!           }
! 
!           return entry;
        }
      }
  
!     /*
!      * Initialize cache and insert anyLocalAddress into the
!      * unknown array with no expiry.
!      */
!     private static void cacheInitIfNeeded() {
!         assert Thread.holdsLock(addressCache);
!         if (addressCacheInit) {
!             return;
!         }
!         unknown_array = new InetAddress[1];
!         unknown_array[0] = impl.anyLocalAddress();
  
!       addressCache.put(impl.anyLocalAddress().getHostName(), 
!                        unknown_array);
  
+         addressCacheInit = true;
+     }
+ 
+     /*
+      * Cache the given hostname and address.
+      */
+     private static void cacheAddress(String hostname, Object address,
+                                    boolean success) {
+ 
        synchronized (addressCache) {
!           cacheInitIfNeeded();
!           if (success) {
!               addressCache.put(hostname, address);
            } else {
!               negativeCache.put(hostname, address);
            }
        }
      }
  
+     /*
+      * Lookup hostname in cache (positive & negative cache). If
+      * found return address, null if not found.
+      */
      private static Object getCachedAddress(String hostname) {
          hostname = hostname.toLowerCase();
  
!       // search both positive & negative caches 
  
        synchronized (addressCache) {
!           CacheEntry entry;
  
!           cacheInitIfNeeded();
  
!           entry = (CacheEntry)addressCache.get(hostname);
            if (entry == null) {
!               entry = (CacheEntry)negativeCache.get(hostname);
            }
! 
!           if (entry != null) {
!               return entry.address;
!           }
!       }
! 
!       // not found
!       return null;
      }
  
      static {
###@###.### 2002-09-19
                                     
2002-09-19
EVALUATION


Yes, there is a bug here - when flushing expired entries from
the cache we bail out when a non-expired entry is encountered.
The result is that we can falsely report stale results (in the
case of positive lookups), or continue to report UHE for expired
negative lookups. Too late to fix in hopper but will fix in mantis.

Note that the problem isn't too serious as caching of negative 
lookups can be disabled via the specified 
networkaddress.cache.negative property (ie: in lib/security/java.security
you can set networkaddress.cache.negative to 0).

###@###.### 2002-05-29

The positive & negative caches are now seperated so we can flush expired
entries from the negative cache without a complete iteration over the
entries.
###@###.### 2002-09-19
                                     
2002-09-19



Hardware and Software, Engineered to Work Together