diff --git a/library/src/main/java/com/alibaba/dcm/DnsCacheEntry.java b/library/src/main/java/com/alibaba/dcm/DnsCacheEntry.java index 118d0a9d..2a1597b1 100644 --- a/library/src/main/java/com/alibaba/dcm/DnsCacheEntry.java +++ b/library/src/main/java/com/alibaba/dcm/DnsCacheEntry.java @@ -24,9 +24,13 @@ public String getHost() { } public String[] getIps() { - String[] copy = new String[ips.length]; - System.arraycopy(ips, 0, copy, 0, ips.length); // defensive copy - return copy; + if (ips != null) { + String[] copy = new String[ips.length]; + System.arraycopy(ips, 0, copy, 0, ips.length); // defensive copy + return copy; + } else { + return null; + } } public String getIp() { diff --git a/library/src/main/java/com/alibaba/dcm/DnsCacheManipulator.java b/library/src/main/java/com/alibaba/dcm/DnsCacheManipulator.java index 48fcce8c..662a0c9e 100644 --- a/library/src/main/java/com/alibaba/dcm/DnsCacheManipulator.java +++ b/library/src/main/java/com/alibaba/dcm/DnsCacheManipulator.java @@ -1,6 +1,9 @@ package com.alibaba.dcm; import com.alibaba.dcm.internal.InetAddressCacheUtil; +import com.alibaba.dcm.internal.InetAddressJdk9PlusCacheUtil; +import com.alibaba.dcm.internal.JavaVersion; +import com.alibaba.dcm.internal.JavaVersionuUtil; import java.io.InputStream; import java.util.Arrays; @@ -34,7 +37,11 @@ public class DnsCacheManipulator { */ public static void setDnsCache(String host, String... ips) { try { - InetAddressCacheUtil.setInetAddressCache(host, ips, NEVER_EXPIRATION); + if (JavaVersionuUtil.CURRENT_JAVA_VERSION.isLessThenOrEqual(JavaVersion.JDK8.getVersionNum())) { + InetAddressCacheUtil.setInetAddressCache(host, ips, NEVER_EXPIRATION); + } else { + InetAddressJdk9PlusCacheUtil.setInetAddressCache(host, ips, NEVER_EXPIRATION); + } } catch (Exception e) { final String message = String.format("Fail to setDnsCache for host %s ip %s, cause: %s", host, Arrays.toString(ips), e.toString()); @@ -52,7 +59,12 @@ public static void setDnsCache(String host, String... ips) { */ public static void setDnsCache(long expireMillis, String host, String... ips) { try { - InetAddressCacheUtil.setInetAddressCache(host, ips, System.currentTimeMillis() + expireMillis); + if (JavaVersionuUtil.CURRENT_JAVA_VERSION.isLessThenOrEqual(JavaVersion.JDK8.getVersionNum())) { + InetAddressCacheUtil.setInetAddressCache(host, ips, System.currentTimeMillis() + expireMillis); + } else { + //need nanos to mills + InetAddressJdk9PlusCacheUtil.setInetAddressCache(host, ips, System.nanoTime() + expireMillis * 1000000); + } } catch (Exception e) { final String message = String.format("Fail to setDnsCache for host %s ip %s expireMillis %s, cause: %s", host, Arrays.toString(ips), expireMillis, e.toString()); @@ -133,7 +145,11 @@ public static void loadDnsCacheConfig(String propertiesFileName) { @Nullable public static DnsCacheEntry getDnsCache(String host) { try { - return InetAddressCacheUtil.getInetAddressCache(host); + if (JavaVersionuUtil.CURRENT_JAVA_VERSION.isLessThenOrEqual(JavaVersion.JDK8.getVersionNum())) { + return InetAddressCacheUtil.getInetAddressCache(host); + } else { + return InetAddressJdk9PlusCacheUtil.getInetAddressCache(host); + } } catch (Exception e) { throw new DnsCacheManipulatorException("Fail to getDnsCache, cause: " + e.toString(), e); } @@ -161,7 +177,11 @@ public static List getAllDnsCache() { */ public static List listDnsCache() { try { - return InetAddressCacheUtil.listInetAddressCache().getCache(); + if (JavaVersionuUtil.CURRENT_JAVA_VERSION.isLessThenOrEqual(JavaVersion.JDK8.getVersionNum())) { + return InetAddressCacheUtil.listInetAddressCache().getCache(); + } else { + return InetAddressJdk9PlusCacheUtil.listInetAddressCache().getCache(); + } } catch (Exception e) { throw new DnsCacheManipulatorException("Fail to listDnsCache, cause: " + e.toString(), e); } @@ -176,7 +196,11 @@ public static List listDnsCache() { */ public static DnsCache getWholeDnsCache() { try { - return InetAddressCacheUtil.listInetAddressCache(); + if (JavaVersionuUtil.CURRENT_JAVA_VERSION.isLessThenOrEqual(JavaVersion.JDK8.getVersionNum())) { + return InetAddressCacheUtil.listInetAddressCache(); + } else { + return InetAddressJdk9PlusCacheUtil.listInetAddressCache(); + } } catch (Exception e) { throw new DnsCacheManipulatorException("Fail to getWholeDnsCache, cause: " + e.toString(), e); } @@ -191,7 +215,11 @@ public static DnsCache getWholeDnsCache() { */ public static void removeDnsCache(String host) { try { - InetAddressCacheUtil.removeInetAddressCache(host); + if (JavaVersionuUtil.CURRENT_JAVA_VERSION.isLessThenOrEqual(JavaVersion.JDK8.getVersionNum())) { + InetAddressCacheUtil.removeInetAddressCache(host); + } else { + InetAddressJdk9PlusCacheUtil.removeInetAddressCache(host); + } } catch (Exception e) { final String message = String.format("Fail to removeDnsCache for host %s, cause: %s", host, e.toString()); throw new DnsCacheManipulatorException(message, e); @@ -205,7 +233,11 @@ public static void removeDnsCache(String host) { */ public static void clearDnsCache() { try { - InetAddressCacheUtil.clearInetAddressCache(); + if (JavaVersionuUtil.CURRENT_JAVA_VERSION.isLessThenOrEqual(JavaVersion.JDK8.getVersionNum())) { + InetAddressCacheUtil.clearInetAddressCache(); + } else { + InetAddressJdk9PlusCacheUtil.clearInetAddressCache(); + } } catch (Exception e) { throw new DnsCacheManipulatorException("Fail to clearDnsCache, cause: " + e.toString(), e); } diff --git a/library/src/main/java/com/alibaba/dcm/internal/InetAddressJdk9PlusCacheUtil.java b/library/src/main/java/com/alibaba/dcm/internal/InetAddressJdk9PlusCacheUtil.java new file mode 100644 index 00000000..40f77608 --- /dev/null +++ b/library/src/main/java/com/alibaba/dcm/internal/InetAddressJdk9PlusCacheUtil.java @@ -0,0 +1,271 @@ +package com.alibaba.dcm.internal; + +import com.alibaba.dcm.DnsCache; +import com.alibaba.dcm.DnsCacheEntry; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.*; + +import static com.alibaba.dcm.internal.InetAddressCacheUtil.isDnsCacheEntryExpired; +import static com.alibaba.dcm.internal.InetAddressCacheUtil.toInetAddressArray; + +/** + * @author dzg + * @since 2020/4/9 + */ +public class InetAddressJdk9PlusCacheUtil { + /** + * recorder jvm start timestamp point + */ + private static final long JVM_START_NANO_SECONDS = System.nanoTime(); + private static final long JVM_START_MILL_SECONDS = System.currentTimeMillis(); + + /** + * jdk9+ not Need convert host to lowercase, see {@link java.net.InetAddress#CachedAddresses(String, InetAddress[], boolean)}. + * expiration // time of expiry (in terms of System.nanoTime()) + */ + public static void setInetAddressCache(String host, String[] ips, long expiration) + throws UnknownHostException, + IllegalAccessException, InstantiationException, InvocationTargetException, + ClassNotFoundException, NoSuchFieldException { + Object entry = newCachedAddresses(host, ips, expiration); + + synchronized (getCacheAndExpirySetFieldOfInetAddress0()) { + getCacheFieldOfInetAddress().put(host, entry); + addExpirySetFieldOfInetAddressByHost(entry); + } + } + + public static void removeInetAddressCache(String host) + throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { + + synchronized (getCacheAndExpirySetFieldOfInetAddress0()) { + getCacheFieldOfInetAddress().remove(host); + removeExpirySetFieldOfInetAddressByHost(host); + } + } + + + /** + * @param host remove this host from expirySet + */ + static void removeExpirySetFieldOfInetAddressByHost(String host) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { + NavigableSet expirySetFieldOfInetAddress = getExpirySetFieldOfInetAddress(); + Iterator iterator = expirySetFieldOfInetAddress.iterator(); + Field hostField = getHostFieldOfInetAddress$CacheAddress(); + while (iterator.hasNext()) { + Object expiry = iterator.next(); + if (hostField.get(expiry).equals(host)) { + iterator.remove(); + } + } + } + + /** + * @param entry add to expirySet + */ + static void addExpirySetFieldOfInetAddressByHost(Object entry) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { + NavigableSet expirySetFieldOfInetAddress = getExpirySetFieldOfInetAddress(); + expirySetFieldOfInetAddress.add(entry); + } + + static Field getHostFieldOfInetAddress$CacheAddress() throws ClassNotFoundException, NoSuchFieldException { + if (hostFieldOfInetAddress$CacheAddress == null) { + synchronized (InetAddressJdk9PlusCacheUtil.class) { + if (hostFieldOfInetAddress$CacheAddress == null) { + String className = "java.net.InetAddress$CachedAddresses"; + Class clazz = Class.forName(className); + hostFieldOfInetAddress$CacheAddress = clazz.getDeclaredField("host"); + hostFieldOfInetAddress$CacheAddress.setAccessible(true); + } + } + } + return hostFieldOfInetAddress$CacheAddress; + } + + static Object newCachedAddresses(String host, String[] ips, long expiration) throws ClassNotFoundException, UnknownHostException, IllegalAccessException, InvocationTargetException, InstantiationException { + String className = "java.net.InetAddress$CachedAddresses"; + Class clazz = Class.forName(className); + // InetAddress.CachedAddresses has only a constructor: + // - for jdk 9-jdk12, constructor signature is CachedAddresses(String host, InetAddress[] inetAddresses, long expiryTime) + // code in jdk 9-jdk12: + // http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/65464a307408/src/java.base/share/classes/java/net/InetAddress.java#783 + Constructor constructor = clazz.getDeclaredConstructors()[0]; + constructor.setAccessible(true); + return constructor.newInstance(host, toInetAddressArray(host, ips), expiration); + } + + /** + * @return {@link InetAddress#cache} + */ + @SuppressWarnings("unchecked") + @GuardedBy("getCacheAndExpirySetFieldOfInetAddress0()") + static Map getCacheFieldOfInetAddress() throws NoSuchFieldException, IllegalAccessException { + return (Map) getCacheAndExpirySetFieldOfInetAddress0()[0]; + } + + /** + * @return {@link InetAddress#expirySet} + */ + @SuppressWarnings("unchecked") + @GuardedBy("getCacheAndExpirySetFieldOfInetAddress0()") + static NavigableSet getExpirySetFieldOfInetAddress() throws NoSuchFieldException, IllegalAccessException { + return (NavigableSet) getCacheAndExpirySetFieldOfInetAddress0()[1]; + } + + static volatile Object[] ADDRESS_CACHE_AND_EXPIRY_SET = null; + + /** + * @return {@link InetAddress#cache} and {@link InetAddress#expirySet} + */ + static Object[] getCacheAndExpirySetFieldOfInetAddress0() throws NoSuchFieldException, IllegalAccessException { + if (ADDRESS_CACHE_AND_EXPIRY_SET == null) { + synchronized (InetAddressJdk9PlusCacheUtil.class) { + if (ADDRESS_CACHE_AND_EXPIRY_SET == null) { + final Field cacheField = InetAddress.class.getDeclaredField("cache"); + cacheField.setAccessible(true); + + final Field expirySetField = InetAddress.class.getDeclaredField("expirySet"); + expirySetField.setAccessible(true); + + ADDRESS_CACHE_AND_EXPIRY_SET = new Object[]{ + cacheField.get(InetAddress.class), + expirySetField.get(InetAddress.class) + }; + } + } + } + return ADDRESS_CACHE_AND_EXPIRY_SET; + } + + public static void clearInetAddressCache() throws NoSuchFieldException, IllegalAccessException { + synchronized (getCacheAndExpirySetFieldOfInetAddress0()) { + getCacheFieldOfInetAddress().clear(); + getExpirySetFieldOfInetAddress().clear(); + } + } + + @Nullable + public static DnsCacheEntry getInetAddressCache(String host) + throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + + final Object cacheAddress; + synchronized (getCacheAndExpirySetFieldOfInetAddress0()) { + cacheAddress = getCacheFieldOfInetAddress().get(host); + } + + if (null == cacheAddress) { + return null; + } + + final DnsCacheEntry dnsCacheEntry = inetAddress$CacheAddress2DnsCacheEntry(host, cacheAddress); + if (dnsCacheEntry.getIps() != null && isDnsCacheEntryExpired(dnsCacheEntry.getHost())) { + return null; + } + return dnsCacheEntry; + } + + public static DnsCache listInetAddressCache() + throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException { + + final Map cache; + final NavigableSet negativeCache; + synchronized (getCacheAndExpirySetFieldOfInetAddress0()) { + cache = getCacheFieldOfInetAddress(); + negativeCache = getExpirySetFieldOfInetAddress(); + } + + List retCache = new ArrayList(); + for (Map.Entry entry : cache.entrySet()) { + final String host = entry.getKey(); + + if (isDnsCacheEntryExpired(host)) { // exclude expired entries! + continue; + } + DnsCacheEntry dnsCacheEntry = inetAddress$CacheAddress2DnsCacheEntry(host, entry.getValue()); + if (dnsCacheEntry.getIps() != null) { + retCache.add(dnsCacheEntry); + } + } + List retNegativeCache = new ArrayList(); + for (Object entry : negativeCache) { + final String host = (String) getHostFieldOfInetAddress$CacheAddress().get(entry); + DnsCacheEntry dnsCacheEntry = inetAddress$CacheAddress2DnsCacheEntry(host, entry); + if (dnsCacheEntry.getIps() == null) { + retNegativeCache.add(dnsCacheEntry); + } + } + return new DnsCache(retCache, retNegativeCache); + } + + static volatile Field hostFieldOfInetAddress$CacheAddress = null; + static volatile Method inetAddressesFieldOfInetAddress$CacheAddress = null; + static volatile Field expiryTimeFieldOfInetAddress$CacheAddress = null; + static volatile Method reqAddrFieldOfInetAddress$CacheAddress = null; + + private static final String NAME_SERVICE_ADDRESS = "NameServiceAddresses"; + private static final String CACHED_ADDRESS = "CachedAddresses"; + private static final Long NEVER_EXPIRY = Long.MAX_VALUE; + + static DnsCacheEntry inetAddress$CacheAddress2DnsCacheEntry(String host, Object cacheAddress) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + Class addressClass = cacheAddress.getClass(); + if (inetAddressesFieldOfInetAddress$CacheAddress == null || expiryTimeFieldOfInetAddress$CacheAddress == null || reqAddrFieldOfInetAddress$CacheAddress == null) { + synchronized (InetAddressJdk9PlusCacheUtil.class) { + Method get = addressClass.getDeclaredMethod("get"); + get.setAccessible(true); + if (addressClass.getName().contains(NAME_SERVICE_ADDRESS)) { + if (reqAddrFieldOfInetAddress$CacheAddress == null) { + reqAddrFieldOfInetAddress$CacheAddress = get; + } + } else if (addressClass.getName().contains(CACHED_ADDRESS)) { + if (inetAddressesFieldOfInetAddress$CacheAddress == null) { + inetAddressesFieldOfInetAddress$CacheAddress = get; + } + if (expiryTimeFieldOfInetAddress$CacheAddress == null) { + Field inetAddressesFiled = addressClass.getDeclaredField("expiryTime"); + inetAddressesFiled.setAccessible(true); + expiryTimeFieldOfInetAddress$CacheAddress = inetAddressesFiled; + } + } else { + throw new IllegalStateException("JDK add new child class " + addressClass.getName() + + " for class InetAddress.Addresses, report bug for dns-cache-manipulator lib!"); + } + + } + + } + InetAddress[] addresses; + long expiration; + if (addressClass.getName().contains(CACHED_ADDRESS)) { + long expirationNanos = (Long) expiryTimeFieldOfInetAddress$CacheAddress.get(cacheAddress); + //expiration timestamp convert + expiration = (expirationNanos - JVM_START_NANO_SECONDS) / 1000000 + JVM_START_MILL_SECONDS; + try { + addresses = (InetAddress[]) inetAddressesFieldOfInetAddress$CacheAddress.invoke(cacheAddress); + } catch (Exception e) { + addresses = null; + } + } else { + addresses = (InetAddress[]) reqAddrFieldOfInetAddress$CacheAddress.invoke(cacheAddress); + expiration = NEVER_EXPIRY; + } + String[] ips = null; + if (addresses != null) { + ips = new String[addresses.length]; + for (int i = 0; i < addresses.length; i++) { + ips[i] = addresses[i].getHostAddress(); + } + } + return new DnsCacheEntry(host, ips, new Date(expiration)); + } + + private InetAddressJdk9PlusCacheUtil() { + } +} diff --git a/library/src/main/java/com/alibaba/dcm/internal/JavaVersion.java b/library/src/main/java/com/alibaba/dcm/internal/JavaVersion.java new file mode 100644 index 00000000..09b86d43 --- /dev/null +++ b/library/src/main/java/com/alibaba/dcm/internal/JavaVersion.java @@ -0,0 +1,36 @@ +package com.alibaba.dcm.internal; + +/** + * @author dzg + * @since 2020/4/9 + */ +public enum JavaVersion { + /** + * jdk版本 + */ + JDK6(1.6D), + JDK7(1.7D), + JDK8(1.8D), + JDK9(9.0D), + JDK10(10.0D), + JDK11(11.0D), + JDK12(12.0D), + JDK13(13.0D), + JDK14(14.0D), + JDK15(15.0D), + JDK16(16.0D), + JDK17(17.0D); + private final double versionNum; + + JavaVersion(double versionNum) { + this.versionNum = versionNum; + } + + public double getVersionNum() { + return versionNum; + } + + public boolean isLessThenOrEqual(double otherVersionNum) { + return this.versionNum <= otherVersionNum; + } +} \ No newline at end of file diff --git a/library/src/main/java/com/alibaba/dcm/internal/JavaVersionuUtil.java b/library/src/main/java/com/alibaba/dcm/internal/JavaVersionuUtil.java new file mode 100644 index 00000000..ce938ef8 --- /dev/null +++ b/library/src/main/java/com/alibaba/dcm/internal/JavaVersionuUtil.java @@ -0,0 +1,37 @@ +package com.alibaba.dcm.internal; + +/** + * @author dzg + * @since 2020/4/9 + */ +public class JavaVersionuUtil { + + public static final JavaVersion CURRENT_JAVA_VERSION; + + static { + CURRENT_JAVA_VERSION = parseCurrentJavaVersion(); + } + + private static JavaVersion parseCurrentJavaVersion() { + String javaVersionTmp = System.getProperty("java.version"); + String[] split = javaVersionTmp.split("\\.", 3); + String javaVersion = "0"; + if (split.length <= 0) { + throw new IllegalStateException("get current java version failed"); + } else if (split.length == 1) { + javaVersion = split[0]; + } else { + javaVersion = split[0] + '.' + split[1]; + } + double javaVersionNum = Double.parseDouble(javaVersion); + JavaVersion currentVersion = JavaVersion.JDK6; + JavaVersion[] javaVersions = JavaVersion.values(); + for (JavaVersion version : javaVersions) { + if (version.isLessThenOrEqual(javaVersionNum)) { + currentVersion = version; + } + } + return currentVersion; + } + +} diff --git a/library/src/test/java/com/alibaba/dcm/DnsCacheManipulatorTest.java b/library/src/test/java/com/alibaba/dcm/DnsCacheManipulatorTest.java index 87f61b74..569e034b 100644 --- a/library/src/test/java/com/alibaba/dcm/DnsCacheManipulatorTest.java +++ b/library/src/test/java/com/alibaba/dcm/DnsCacheManipulatorTest.java @@ -108,7 +108,13 @@ public void test_setDnsCache_getAllDnsCache() throws Exception { final List expected = Arrays.asList( new DnsCacheEntry(host.toLowerCase(), new String[]{IP3}, new Date(Long.MAX_VALUE))); - assertEquals(expected, allDnsCacheEntries); + assertEquals(expected.size(), allDnsCacheEntries.size()); + DnsCacheEntry expectedDnsCacheEntry = expected.get(0); + DnsCacheEntry dnsCacheEntry = allDnsCacheEntries.get(0); + assertEquals(expectedDnsCacheEntry.getHost().toLowerCase(), dnsCacheEntry.getHost().toLowerCase()); + assertEquals(expectedDnsCacheEntry.getIp(), dnsCacheEntry.getIp()); + long now = currentTimeMillis(); + assertEquals(expectedDnsCacheEntry.getExpiration().getTime() - now > 315360000000L, dnsCacheEntry.getExpiration().getTime() - now > 315360000000L); assertTrue(DnsCacheManipulator.getWholeDnsCache().getNegativeCache().isEmpty()); } @@ -158,7 +164,7 @@ public void test_setNotExistedDomain_RemoveThenReLookupAndNotExisted() throws Ex final List negativeCache = DnsCacheManipulator.getWholeDnsCache().getNegativeCache(); assertEquals(1, negativeCache.size()); - assertEquals(DOMAIN_NOT_EXISTED.toLowerCase(), negativeCache.get(0).getHost()); + assertEquals(DOMAIN_NOT_EXISTED.toLowerCase(), negativeCache.get(0).getHost().toLowerCase()); } private static void assertDomainNotExisted() { @@ -193,22 +199,31 @@ public void test_setNotExistedDomain_canExpire_thenReLookupAndNotExisted() throw final List negativeCache = DnsCacheManipulator.getWholeDnsCache().getNegativeCache(); assertEquals(1, negativeCache.size()); - assertEquals(DOMAIN_NOT_EXISTED.toLowerCase(), negativeCache.get(0).getHost()); + assertEquals(DOMAIN_NOT_EXISTED.toLowerCase(), negativeCache.get(0).getHost().toLowerCase()); } @Test public void test_multi_ips_in_config_file() throws Exception { + long now = currentTimeMillis(); DnsCacheManipulator.loadDnsCacheConfig("dns-cache-multi-ips.properties"); final String host = "www.hello-multi-ips.com"; DnsCacheEntry entry = new DnsCacheEntry(host, new String[]{"42.42.41.1", "42.42.41.2"}, new Date(Long.MAX_VALUE)); - assertEquals(entry, DnsCacheManipulator.getDnsCache(host)); + DnsCacheEntry dnsCache = DnsCacheManipulator.getDnsCache(host); + assertEquals(entry.getHost(), dnsCache.getHost()); + assertEquals(entry.getIp(), dnsCache.getIp()); + assertEquals(entry.getExpiration().getTime() - now > 315360000000L, dnsCache.getExpiration().getTime() - now > 315360000000L); + final String hostLoose = "www.hello-multi-ips-loose.com"; DnsCacheEntry entryLoose = new DnsCacheEntry(hostLoose, new String[]{"42.42.41.1", "42.42.41.2", "42.42.41.3", "42.42.41.4"}, new Date(Long.MAX_VALUE)); - assertEquals(entryLoose, DnsCacheManipulator.getDnsCache(hostLoose)); + DnsCacheEntry dnsCacheLoose = DnsCacheManipulator.getDnsCache(hostLoose); + assertEquals(entryLoose.getHost(), dnsCacheLoose.getHost()); + assertEquals(entryLoose.getIp(), dnsCacheLoose.getIp()); + assertEquals(entryLoose.getExpiration().getTime() - now > 315360000000L, dnsCacheLoose.getExpiration().getTime() - now > 315360000000L); + } @Test