diff --git a/src/java.base/share/classes/java/net/InetAddress.java b/src/java.base/share/classes/java/net/InetAddress.java index 8a4196644eb..fcc2e270026 100644 --- a/src/java.base/share/classes/java/net/InetAddress.java +++ b/src/java.base/share/classes/java/net/InetAddress.java @@ -52,6 +52,7 @@ import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicLong; import java.util.Arrays; +import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Stream; @@ -191,9 +192,9 @@ * <p> If the default behavior is not desired, then a Java security property * can be set to a different Time-to-live (TTL) value for positive * caching. Likewise, a system admin can configure a different - * negative caching TTL value when needed. + * negative caching TTL value when needed or extend the usage of the stale data. * - * <p> Two Java security properties control the TTL values used for + * <p> Three Java security properties control the TTL values used for * positive and negative host name resolution caching: * * <dl style="margin-left:2em"> @@ -205,6 +206,25 @@ * <p> * A value of -1 indicates "cache forever". * </dd> + * <dt><b>networkaddress.cache.stale.ttl</b></dt> + * <dd>Indicates the caching policy for stale names. The value is specified as + * an integer to indicate the number of seconds that stale names will be kept in + * the cache. A name is considered stale if the TTL has expired and an attempt + * to lookup the host name again was not successful. This property is useful if + * it is preferable to use a stale name rather than fail due to an unsuccessful + * lookup. The default setting is to cache for an implementation specific period + * of time. + * <p> + * If the value of this property is larger than "networkaddress.cache.ttl" then + * "networkaddress.cache.ttl" will be used as a refresh interval of the name in + * the cache. For example, if this property is set to 1 day and + * "networkaddress.cache.ttl" is set to 30 seconds, then the positive response + * will be cached for 1 day but an attempt to refresh it will be done every + * 30 seconds. + * <p> + * A value of 0 (zero) or if the property is not set means do not use stale + * names. Negative values are ignored. + * </dd> * <dt><b>networkaddress.cache.negative.ttl</b> (default: 10)</dt> * <dd>Indicates the caching policy for un-successful name lookups * from the name service. The value is specified as an integer to @@ -933,7 +953,7 @@ public String toString() { // CachedAddresses that have to expire are kept ordered in this NavigableSet // which is scanned on each access - private static final NavigableSet<CachedAddresses> expirySet = + private static final NavigableSet<CachedLookup> expirySet = new ConcurrentSkipListSet<>(); // common interface @@ -941,15 +961,22 @@ private interface Addresses { InetAddress[] get() throws UnknownHostException; } - // a holder for cached addresses with required metadata - private static final class CachedAddresses implements Addresses, Comparable<CachedAddresses> { + /** + * A cached result of a name service lookup. The result can be either valid + * addresses or invalid (ie a failed lookup) containing no addresses. + */ + private static class CachedLookup implements Addresses, Comparable<CachedLookup> { private static final AtomicLong seq = new AtomicLong(); final String host; - final InetAddress[] inetAddresses; - final long expiryTime; // time of expiry (in terms of System.nanoTime()) + volatile InetAddress[] inetAddresses; + /** + * Time of expiry (in terms of System.nanoTime()). Can be modified only + * when the record is not added to the "expirySet". + */ + volatile long expiryTime; final long id = seq.incrementAndGet(); // each instance is unique - CachedAddresses(String host, InetAddress[] inetAddresses, long expiryTime) { + CachedLookup(String host, InetAddress[] inetAddresses, long expiryTime) { this.host = host; this.inetAddresses = inetAddresses; this.expiryTime = expiryTime; @@ -964,7 +991,7 @@ public InetAddress[] get() throws UnknownHostException { } @Override - public int compareTo(CachedAddresses other) { + public int compareTo(CachedLookup other) { // natural order is expiry time - // compare difference of expiry times rather than // expiry times directly, to avoid possible overflow. @@ -975,6 +1002,106 @@ public int compareTo(CachedAddresses other) { // ties are broken using unique id return Long.compare(this.id, other.id); } + + /** + * Checks if the current cache record is expired or not. Expired records + * are removed from the expirySet and cache. + * + * @return {@code true} if the record was removed + */ + public boolean tryRemoveExpiredAddress(long now) { + // compare difference of time instants rather than + // time instants directly, to avoid possible overflow. + // (see System.nanoTime() recommendations...) + if ((expiryTime - now) < 0L) { + // ConcurrentSkipListSet uses weakly consistent iterator, + // so removing while iterating is OK... + if (expirySet.remove(this)) { + // ... remove from cache + cache.remove(host, this); + } + return true; + } + return false; + } + } + + /** + * A cached valid lookup containing addresses whose validity may be + * temporarily extended by an additional stale period pending the mapping + * being refreshed or updated. + */ + private static final class ValidCachedLookup extends CachedLookup { + /** + * Time to refresh (in terms of System.nanoTime()). + */ + private volatile long refreshTime; + /** + * For how long the stale data should be used after TTL expiration. + * Initially equal to the expiryTime, but increased over time after each + * successful lookup. + */ + private volatile long staleTime; + + /** + * only one thread is doing lookup to name service + * for particular host at any time. + */ + private final Lock lookupLock = new ReentrantLock(); + + ValidCachedLookup(String host, InetAddress[] inetAddresses, + long staleTime, long refreshTime) + { + super(host, inetAddresses, staleTime); + this.refreshTime = refreshTime; + this.staleTime = staleTime; + } + + @Override + public InetAddress[] get() { + long now = System.nanoTime(); + if ((refreshTime - now) < 0L && lookupLock.tryLock()) { + try { + // cachePolicy is in [s] - we need [ns] + refreshTime = now + InetAddressCachePolicy.get() * 1000_000_000L; + // getAddressesFromNameService returns non-empty/non-null value + inetAddresses = getAddressesFromNameService(host); + // don't update the "expirySet", will do that later + staleTime = refreshTime + InetAddressCachePolicy.getStale() * 1000_000_000L; + } catch (UnknownHostException ignore) { + } finally { + lookupLock.unlock(); + } + } + return inetAddresses; + } + + /** + * Overrides the parent method to skip deleting the record from the + * cache if the stale data can still be used. Note to update the + * "expiryTime" field we have to remove the record from the expirySet + * and add it back. It is not necessary to remove/add it here, we can do + * that in the "get()" method above, but extracting it minimizes + * contention on "expirySet". + */ + @Override + public boolean tryRemoveExpiredAddress(long now) { + // compare difference of time instants rather than + // time instants directly, to avoid possible overflow. + // (see System.nanoTime() recommendations...) + if ((expiryTime - now) < 0L) { + if ((staleTime - now) < 0L) { + return super.tryRemoveExpiredAddress(now); + } + // ConcurrentSkipListSet uses weakly consistent iterator, + // so removing while iterating is OK... + if (expirySet.remove(this)) { + expiryTime = staleTime; + expirySet.add(this); + } + } + return false; + } } // a name service lookup based Addresses implementation which replaces itself @@ -1021,18 +1148,33 @@ public InetAddress[] get() throws UnknownHostException { if (cachePolicy == InetAddressCachePolicy.NEVER) { cache.remove(host, this); } else { - CachedAddresses cachedAddresses = new CachedAddresses( - host, - inetAddresses, - cachePolicy == InetAddressCachePolicy.FOREVER - ? 0L - // cachePolicy is in [s] - we need [ns] - : System.nanoTime() + 1000_000_000L * cachePolicy - ); - if (cache.replace(host, this, cachedAddresses) && + long now = System.nanoTime(); + long expiryTime = + cachePolicy == InetAddressCachePolicy.FOREVER ? + 0L + // cachePolicy is in [s] - we need [ns] + : now + 1000_000_000L * cachePolicy; + CachedLookup cachedLookup; + if (InetAddressCachePolicy.getStale() > 0 && + ex == null && expiryTime > 0) + { + long refreshTime = expiryTime; + // staleCachePolicy is in [s] - we need [ns] + expiryTime = refreshTime + 1000_000_000L * + InetAddressCachePolicy.getStale(); + cachedLookup = new ValidCachedLookup(host, + inetAddresses, + expiryTime, + refreshTime); + } else { + cachedLookup = new CachedLookup(host, + inetAddresses, + expiryTime); + } + if (cache.replace(host, this, cachedLookup) && cachePolicy != InetAddressCachePolicy.FOREVER) { // schedule expiry - expirySet.add(cachedAddresses); + expirySet.add(cachedLookup); } } if (inetAddresses == null || inetAddresses.length == 0) { @@ -1638,18 +1780,8 @@ private static InetAddress[] getAllByName0(String host, // remove expired addresses from cache - expirySet keeps them ordered // by expiry time so we only need to iterate the prefix of the NavigableSet... long now = System.nanoTime(); - for (CachedAddresses caddrs : expirySet) { - // compare difference of time instants rather than - // time instants directly, to avoid possible overflow. - // (see System.nanoTime() recommendations...) - if ((caddrs.expiryTime - now) < 0L) { - // ConcurrentSkipListSet uses weakly consistent iterator, - // so removing while iterating is OK... - if (expirySet.remove(caddrs)) { - // ... remove from cache - cache.remove(caddrs.host, caddrs); - } - } else { + for (CachedLookup caddrs : expirySet) { + if (!caddrs.tryRemoveExpiredAddress(now)) { // we encountered 1st element that expires in future break; } @@ -1662,7 +1794,7 @@ private static InetAddress[] getAllByName0(String host, } else { addrs = cache.remove(host); if (addrs != null) { - if (addrs instanceof CachedAddresses) { + if (addrs instanceof CachedLookup) { // try removing from expirySet too if CachedAddresses expirySet.remove(addrs); } diff --git a/src/java.base/share/classes/java/net/doc-files/net-properties.html b/src/java.base/share/classes/java/net/doc-files/net-properties.html index ffc65492b51..85af2487bf1 100644 --- a/src/java.base/share/classes/java/net/doc-files/net-properties.html +++ b/src/java.base/share/classes/java/net/doc-files/net-properties.html @@ -1,6 +1,6 @@ <!DOCTYPE HTML> <!-- - Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. This code is free software; you can redistribute it and/or modify it @@ -267,13 +267,22 @@ <H2>Address Cache</H2> policy, while a value of 0 (zero) means no caching. The default value is -1 (forever) if a security manager is installed, and implementation-specific when no security manager is installed.</P> + <LI><P><B>{@systemProperty networkaddress.cache.stale.ttl}</B> (default: see below)<BR> + Value is an integer corresponding to the number of seconds that stale names + will be kept in the cache. A name is considered stale if the TTL has expired + and an attempt to lookup the host name again was not successful. This + property is useful if it is preferable to use a stale name rather than + fail due to an unsuccessful lookup. + A value of 0 (zero) or if the property is not set means do not use stale + names. Negative values are ignored. + The default value is implementation-specific.</P> <LI><P><B>{@systemProperty networkaddress.cache.negative.ttl}</B> (default: {@code 10})<BR> Value is an integer corresponding to the number of seconds an unsuccessful name lookup will be kept in the cache. A value of -1, or any negative value, means “cache forever”, while a value of 0 (zero) means no caching.</P> </UL> -<P>Since these 2 properties are part of the security policy, they are +<P>Since these 3 properties are part of the security policy, they are not set by either the -D option or the {@code System.setProperty()} API, instead they are set as security properties.</P> <a id="Unixdomain"></a> diff --git a/src/java.base/share/classes/sun/net/InetAddressCachePolicy.java b/src/java.base/share/classes/sun/net/InetAddressCachePolicy.java index 8b4673bdd6a..5c127025682 100644 --- a/src/java.base/share/classes/sun/net/InetAddressCachePolicy.java +++ b/src/java.base/share/classes/sun/net/InetAddressCachePolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import java.security.PrivilegedAction; import java.security.Security; +import java.util.concurrent.TimeUnit; @SuppressWarnings("removal") public final class InetAddressCachePolicy { @@ -36,6 +37,12 @@ public final class InetAddressCachePolicy { private static final String cachePolicyPropFallback = "sun.net.inetaddr.ttl"; + // Controls the cache stale policy for successful lookups only + private static final String cacheStalePolicyProp = + "networkaddress.cache.stale.ttl"; + private static final String cacheStalePolicyPropFallback = + "sun.net.inetaddr.stale.ttl"; + // Controls the cache policy for negative lookups only private static final String negativeCachePolicyProp = "networkaddress.cache.negative.ttl"; @@ -59,6 +66,15 @@ public final class InetAddressCachePolicy { */ private static volatile int cachePolicy = FOREVER; + /* The Java-level namelookup cache stale policy: + * + * any positive value: the number of seconds to use the stale names + * zero: do not use stale names + * + * default value is never (NEVER). + */ + private static volatile int staleCachePolicy = NEVER; + /* The Java-level namelookup cache policy for negative lookups: * * -1: caching forever @@ -85,31 +101,7 @@ public final class InetAddressCachePolicy { * Initialize */ static { - - Integer tmp = java.security.AccessController.doPrivileged( - new PrivilegedAction<Integer>() { - public Integer run() { - try { - String tmpString = Security.getProperty(cachePolicyProp); - if (tmpString != null) { - return Integer.valueOf(tmpString); - } - } catch (NumberFormatException ignored) { - // Ignore - } - - try { - String tmpString = System.getProperty(cachePolicyPropFallback); - if (tmpString != null) { - return Integer.decode(tmpString); - } - } catch (NumberFormatException ignored) { - // Ignore - } - return null; - } - }); - + Integer tmp = getProperty(cachePolicyProp, cachePolicyPropFallback); if (tmp != null) { cachePolicy = tmp < 0 ? FOREVER : tmp; propertySet = true; @@ -121,40 +113,60 @@ public Integer run() { cachePolicy = DEFAULT_POSITIVE; } } - tmp = java.security.AccessController.doPrivileged ( - new PrivilegedAction<Integer>() { - public Integer run() { - try { - String tmpString = Security.getProperty(negativeCachePolicyProp); - if (tmpString != null) { - return Integer.valueOf(tmpString); - } - } catch (NumberFormatException ignored) { - // Ignore - } - - try { - String tmpString = System.getProperty(negativeCachePolicyPropFallback); - if (tmpString != null) { - return Integer.decode(tmpString); - } - } catch (NumberFormatException ignored) { - // Ignore - } - return null; - } - }); + tmp = getProperty(negativeCachePolicyProp, + negativeCachePolicyPropFallback); if (tmp != null) { negativeCachePolicy = tmp < 0 ? FOREVER : tmp; propertyNegativeSet = true; } + if (cachePolicy > 0) { + tmp = getProperty(cacheStalePolicyProp, + cacheStalePolicyPropFallback); + if (tmp != null) { + staleCachePolicy = tmp; + } + } + } + + private static Integer getProperty(String cachePolicyProp, + String cachePolicyPropFallback) + { + return java.security.AccessController.doPrivileged( + new PrivilegedAction<Integer>() { + public Integer run() { + try { + String tmpString = Security.getProperty( + cachePolicyProp); + if (tmpString != null) { + return Integer.valueOf(tmpString); + } + } catch (NumberFormatException ignored) { + // Ignore + } + + try { + String tmpString = System.getProperty( + cachePolicyPropFallback); + if (tmpString != null) { + return Integer.decode(tmpString); + } + } catch (NumberFormatException ignored) { + // Ignore + } + return null; + } + }); } public static int get() { return cachePolicy; } + public static int getStale() { + return staleCachePolicy; + } + public static int getNegative() { return negativeCachePolicy; } diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 92bde0749ce..d40710995f0 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -357,6 +357,17 @@ ssl.TrustManagerFactory.algorithm=PKIX # #networkaddress.cache.ttl=-1 +# +# The Java-level namelookup cache stale policy: +# +# any positive value: the number of seconds to use the stale names +# zero: do not use stale names +# negative values are ignored +# +# default value is 0 (NEVER). +# +#networkaddress.cache.stale.ttl=0 + # The Java-level namelookup cache policy for failed lookups: # # any negative value: cache forever diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesCachingTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesCachingTest.java index 4de9e74936e..26dabc00063 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesCachingTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesCachingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,9 +37,27 @@ * @library lib providers/simple * @build test.library/testlib.ResolutionRegistry * simple.provider/impl.SimpleResolverProviderImpl AddressesCachingTest - * @run testng/othervm -Djava.security.properties=${test.src}/NeverCache.props + * @run testng/othervm -Djava.security.properties=${test.src}/props/NeverCache.props * -Dtest.cachingDisabled=true AddressesCachingTest - * @run testng/othervm -Djava.security.properties=${test.src}/ForeverCache.props + * @run testng/othervm -Djava.security.properties=${test.src}/props/ForeverCache.props + * -Dtest.cachingDisabled=false AddressesCachingTest + * @run testng/othervm + * -Djava.security.properties=${test.src}/props/NeverCacheIgnoreMinusStale.props + * -Dtest.cachingDisabled=true AddressesCachingTest + * @run testng/othervm + * -Djava.security.properties=${test.src}/props/NeverCacheIgnorePositiveStale.props + * -Dtest.cachingDisabled=true AddressesCachingTest + * @run testng/othervm + * -Djava.security.properties=${test.src}/props/NeverCacheIgnoreZeroStale.props + * -Dtest.cachingDisabled=true AddressesCachingTest + * @run testng/othervm + * -Djava.security.properties=${test.src}/props/ForeverCacheIgnoreMinusStale.props + * -Dtest.cachingDisabled=false AddressesCachingTest + * @run testng/othervm + * -Djava.security.properties=${test.src}/props/ForeverCacheIgnorePositiveStale.props + * -Dtest.cachingDisabled=false AddressesCachingTest + * @run testng/othervm + * -Djava.security.properties=${test.src}/props/ForeverCacheIgnoreZeroStale.props * -Dtest.cachingDisabled=false AddressesCachingTest */ public class AddressesCachingTest { diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesStaleCachingTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesStaleCachingTest.java new file mode 100644 index 00000000000..a8393221a72 --- /dev/null +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesStaleCachingTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import impl.SimpleResolverProviderImpl; +import org.testng.Assert; +import org.testng.annotations.Test; + + +/* + * @test + * @summary Test that stale InetAddress caching security properties work as + * expected when a custom resolver is installed. + * @library lib providers/simple + * @build test.library/testlib.ResolutionRegistry + * simple.provider/impl.SimpleResolverProviderImpl AddressesStaleCachingTest + * @run testng/othervm -Djava.security.properties=${test.src}/props/CacheStale.props AddressesStaleCachingTest + */ +public class AddressesStaleCachingTest { + + private static class Lookup { + private final byte[] address; + private final long timestamp; + + private Lookup(byte[] address, long timestamp) { + this.address = address; + this.timestamp = timestamp; + } + } + + /** + * Validates successful and unsuccessful lookups when the stale cache is + * enabled. + */ + @Test + public void testRefresh() throws Exception{ + // The first request is to save the data into the cache + Lookup first = doLookup(false, 0); + + Thread.sleep(10000); // intentionally big delay > x2 stale property + // The refreshTime is expired, we will do the successful lookup. + Lookup second = doLookup(false, 0); + Assert.assertNotEquals(first.timestamp, second.timestamp, + "Two lookups are expected"); + + Thread.sleep(10000); // intentionally big delay > x2 stale property + // The refreshTime is expired again, we will do the failed lookup. + Lookup third = doLookup(true, 0); + Assert.assertNotEquals(second.timestamp, third.timestamp, + "Two lookups are expected"); + + // The stale cache is enabled, so we should get valid/same data for + // all requests(even for the failed request). + Assert.assertEquals(first.address, second.address, + "Same address is expected"); + Assert.assertEquals(second.address, third.address, + "Same address is expected"); + } + + /** + * Validates that only one thread is blocked during "refresh", all others + * will continue to use the "stale" data. + */ + @Test + public void testOnlyOneThreadIsBlockedDuringRefresh() throws Exception { + long timeout = System.nanoTime() + TimeUnit.SECONDS.toNanos(12); + doLookup(false, timeout); + Thread.sleep(9000); + + CountDownLatch blockServer = new CountDownLatch(1); + SimpleResolverProviderImpl.setBlocker(blockServer); + + Thread ts[] = new Thread[10]; + CountDownLatch wait9 = new CountDownLatch(ts.length - 1); + CountDownLatch wait10 = new CountDownLatch(ts.length); + CountDownLatch start = new CountDownLatch(ts.length); + for (int i = 0; i < ts.length; i++) { + ts[i] = new Thread(() -> { + start.countDown(); + try { + start.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + doLookup(true, timeout); + wait9.countDown(); + wait10.countDown(); + }); + } + for (Thread t : ts) { + t.start(); + } + if (!wait9.await(10, TimeUnit.SECONDS)) { + blockServer.countDown(); + throw new RuntimeException("Some threads hang"); + } + blockServer.countDown(); + if (!wait10.await(10, TimeUnit.SECONDS)) { + throw new RuntimeException("The last thread hangs"); + } + } + + private static Lookup doLookup(boolean error, long timeout) { + SimpleResolverProviderImpl.setUnreachableServer(error); + try { + byte[] firstAddress = InetAddress.getByName("javaTest.org").getAddress(); + long firstTimestamp = SimpleResolverProviderImpl.getLastLookupTimestamp(); + + byte[] secondAddress = InetAddress.getByName("javaTest.org").getAddress(); + long secondTimestamp = SimpleResolverProviderImpl.getLastLookupTimestamp(); + + Assert.assertEquals(firstAddress, secondAddress, + "Same address is expected"); + if (timeout == 0 || timeout - System.nanoTime() > 0) { + Assert.assertEquals(firstTimestamp, secondTimestamp, + "Only one positive lookup is expected with caching enabled"); + } + return new Lookup(firstAddress, firstTimestamp); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/props/CacheStale.props b/test/jdk/java/net/spi/InetAddressResolverProvider/props/CacheStale.props new file mode 100644 index 00000000000..6e5aa6f0376 --- /dev/null +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/props/CacheStale.props @@ -0,0 +1,3 @@ +networkaddress.cache.ttl=7 +networkaddress.cache.negative.ttl=3 +networkaddress.cache.stale.ttl=30 diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/ForeverCache.props b/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCache.props similarity index 100% rename from test/jdk/java/net/spi/InetAddressResolverProvider/ForeverCache.props rename to test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCache.props diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreMinusStale.props b/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreMinusStale.props new file mode 100644 index 00000000000..041fedbb4c7 --- /dev/null +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreMinusStale.props @@ -0,0 +1,3 @@ +networkaddress.cache.ttl=-1 +networkaddress.cache.negative.ttl=-1 +networkaddress.cache.stale.ttl=-1 diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnorePositiveStale.props b/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnorePositiveStale.props new file mode 100644 index 00000000000..6ceb3100aa3 --- /dev/null +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnorePositiveStale.props @@ -0,0 +1,3 @@ +networkaddress.cache.ttl=-1 +networkaddress.cache.negative.ttl=-1 +networkaddress.cache.stale.ttl=10000 diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreZeroStale.props b/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreZeroStale.props new file mode 100644 index 00000000000..52febf14a94 --- /dev/null +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreZeroStale.props @@ -0,0 +1,3 @@ +networkaddress.cache.ttl=-1 +networkaddress.cache.negative.ttl=-1 +networkaddress.cache.stale.ttl=0 diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/NeverCache.props b/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCache.props similarity index 100% rename from test/jdk/java/net/spi/InetAddressResolverProvider/NeverCache.props rename to test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCache.props diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreMinusStale.props b/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreMinusStale.props new file mode 100644 index 00000000000..e643ed849d9 --- /dev/null +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreMinusStale.props @@ -0,0 +1,3 @@ +networkaddress.cache.ttl=0 +networkaddress.cache.negative.ttl=0 +networkaddress.cache.stale.ttl=-1 diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnorePositiveStale.props b/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnorePositiveStale.props new file mode 100644 index 00000000000..ec1a501ba95 --- /dev/null +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnorePositiveStale.props @@ -0,0 +1,3 @@ +networkaddress.cache.ttl=0 +networkaddress.cache.negative.ttl=0 +networkaddress.cache.stale.ttl=10000 diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreZeroStale.props b/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreZeroStale.props new file mode 100644 index 00000000000..8ead383a681 --- /dev/null +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreZeroStale.props @@ -0,0 +1,3 @@ +networkaddress.cache.ttl=0 +networkaddress.cache.negative.ttl=0 +networkaddress.cache.stale.ttl=0 diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/providers/simple/simple.provider/impl/SimpleResolverProviderImpl.java b/test/jdk/java/net/spi/InetAddressResolverProvider/providers/simple/simple.provider/impl/SimpleResolverProviderImpl.java index 7fde1755084..7fc10423c2a 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/providers/simple/simple.provider/impl/SimpleResolverProviderImpl.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/providers/simple/simple.provider/impl/SimpleResolverProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.logging.Logger; import java.util.stream.Stream; @@ -41,6 +42,8 @@ public class SimpleResolverProviderImpl extends InetAddressResolverProvider { public static ResolutionRegistry registry = new ResolutionRegistry(); private static List<LookupPolicy> LOOKUP_HISTORY = Collections.synchronizedList(new ArrayList<>()); private static volatile long LAST_LOOKUP_TIMESTAMP; + private static volatile boolean unreachableServer; + private static volatile CountDownLatch blocker; private static Logger LOGGER = Logger.getLogger(SimpleResolverProviderImpl.class.getName()); @Override @@ -51,14 +54,27 @@ public InetAddressResolver get(Configuration configuration) { public Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy) throws UnknownHostException { LOGGER.info("Looking-up addresses for '" + host + "'. Lookup characteristics:" + Integer.toString(lookupPolicy.characteristics(), 2)); + if (blocker != null) { + try { + blocker.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } LOOKUP_HISTORY.add(lookupPolicy); LAST_LOOKUP_TIMESTAMP = System.nanoTime(); + if (unreachableServer) { + throw new UnknownHostException("unreachableServer"); + } return registry.lookupHost(host, lookupPolicy); } @Override public String lookupByAddress(byte[] addr) throws UnknownHostException { LOGGER.info("Looking host name for the following address:" + ResolutionRegistry.addressBytesToString(addr)); + if (unreachableServer) { + throw new UnknownHostException("unreachableServer"); + } return registry.lookupAddress(addr); } }; @@ -73,6 +89,14 @@ public static long getLastLookupTimestamp() { return LAST_LOOKUP_TIMESTAMP; } + public static void setUnreachableServer(boolean unreachableServer) { + SimpleResolverProviderImpl.unreachableServer = unreachableServer; + } + + public static void setBlocker(CountDownLatch blocker) { + SimpleResolverProviderImpl.blocker = blocker; + } + public static LookupPolicy lookupPolicyHistory(int position) { if (LOOKUP_HISTORY.isEmpty()) { throw new RuntimeException("No registered lookup policies");