Skip to content

Commit

Permalink
add InetAddress#negativeCache manipulation #17 #19
Browse files Browse the repository at this point in the history
  • Loading branch information
oldratlee committed Apr 19, 2015
1 parent 6db29fc commit 3cb1c68
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 20 deletions.
65 changes: 65 additions & 0 deletions src/main/java/com/alibaba/dcm/DnsCache.java
@@ -0,0 +1,65 @@
package com.alibaba.dcm;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.concurrent.Immutable;

/**
* JVM whole dns cache info, including negative cache.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see DnsCacheEntry
* @since 1.2.0
*/
@Immutable
public class DnsCache implements Serializable {
private static final long serialVersionUID = -8614746635950970028L;

final List<DnsCacheEntry> cache;
final List<DnsCacheEntry> negativeCache;

public DnsCache(List<DnsCacheEntry> cache, List<DnsCacheEntry> negativeCache) {
this.cache = cache;
this.negativeCache = negativeCache;
}

public List<DnsCacheEntry> getCache() {
// defensive copy
return new ArrayList<DnsCacheEntry>(cache);
}

public List<DnsCacheEntry> getNegativeCache() {
// defensive copy
return new ArrayList<DnsCacheEntry>(negativeCache);
}

@Override
public String toString() {
return "DnsCache{" +
"cache=" + cache +
", negativeCache=" + negativeCache +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

DnsCache dnsCache = (DnsCache) o;

if (cache != null ? !cache.equals(dnsCache.cache) : dnsCache.cache != null)
return false;
return !(negativeCache != null ? !negativeCache.equals(dnsCache.negativeCache) : dnsCache.negativeCache != null);

}

@Override
public int hashCode() {
int result = cache != null ? cache.hashCode() : 0;
result = 31 * result + (negativeCache != null ? negativeCache.hashCode() : 0);
return result;
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/alibaba/dcm/DnsCacheEntry.java
Expand Up @@ -8,6 +8,10 @@

import javax.annotation.concurrent.Immutable;

/**
* @author Jerry Lee (oldratlee at gmail dot com)
* @see DnsCache
*/
@Immutable
public final class DnsCacheEntry implements Serializable {
private static final long serialVersionUID = -7476648934387757732L;
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/com/alibaba/dcm/DnsCacheManipulator.java
Expand Up @@ -11,6 +11,8 @@

/**
* Setting dns (in fact dns cache).
* <p/>
* Throw {@link DnsCacheManipulatorException} if operation fail for all methods.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see DnsCacheEntry
Expand Down Expand Up @@ -133,8 +135,37 @@ public static DnsCacheEntry getDnsCache(String host) {
*
* @return dns cache entries
* @throws DnsCacheManipulatorException Operation fail
* @deprecated use {@link #listDnsCache} instead.
*/
@Deprecated
public static List<DnsCacheEntry> getAllDnsCache() {
return listDnsCache();
}

/**
* Get all dns cache entries.
*
* @return dns cache entries
* @throws DnsCacheManipulatorException Operation fail
* @see #getWholeDnsCache()
* @since 1.2.0
*/
public static List<DnsCacheEntry> listDnsCache() {
try {
return InetAddressCacheUtil.listInetAddressCache().getCache();
} catch (Exception e) {
throw new DnsCacheManipulatorException("Fail to getAllDnsCache, cause: " + e.toString(), e);
}
}

/**
* Get whole dns cache info.
*
* @return dns cache entries
* @throws DnsCacheManipulatorException Operation fail
* @since 1.2.0
*/
public static DnsCache getWholeDnsCache() {
try {
return InetAddressCacheUtil.listInetAddressCache();
} catch (Exception e) {
Expand Down
89 changes: 69 additions & 20 deletions src/main/java/com/alibaba/dcm/internal/InetAddressCacheUtil.java
@@ -1,5 +1,6 @@
package com.alibaba.dcm.internal;

import com.alibaba.dcm.DnsCache;
import com.alibaba.dcm.DnsCacheEntry;

import java.lang.reflect.Constructor;
Expand All @@ -14,6 +15,7 @@
import java.util.Map;
import java.util.regex.Pattern;

import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;

/**
Expand Down Expand Up @@ -41,10 +43,11 @@ public static void setInetAddressCache(String host, String[] ips, long expiratio
IllegalAccessException, InstantiationException, InvocationTargetException,
ClassNotFoundException, NoSuchFieldException {
host = host.toLowerCase();
Object entry = createCacheEntry(host, ips, expiration);
Object entry = newCacheEntry(host, ips, expiration);

synchronized (getAddressCacheFieldOfInetAddress()) {
getCacheFiledOfInetAddress$CacheEntry().put(host, entry);
getCacheFiledOfAddressCacheFiledOfInetAddress().put(host, entry);
getCacheFiledOfNegativeCacheFiledOfInetAddress().remove(host);
}
}

Expand All @@ -53,11 +56,12 @@ public static void removeInetAddressCache(String host)
host = host.toLowerCase();

synchronized (getAddressCacheFieldOfInetAddress()) {
getCacheFiledOfInetAddress$CacheEntry().remove(host);
getCacheFiledOfAddressCacheFiledOfInetAddress().remove(host);
getCacheFiledOfNegativeCacheFiledOfInetAddress().remove(host);
}
}

static Object createCacheEntry(String host, String[] ips, long expiration)
static Object newCacheEntry(String host, String[] ips, long expiration)
throws UnknownHostException, ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException, InstantiationException {
String className = "java.net.InetAddress$CacheEntry";
Expand All @@ -80,35 +84,71 @@ static Object createCacheEntry(String host, String[] ips, long expiration)
/**
* @return {@link InetAddress.Cache#cache} in {@link InetAddress#addressCache}
*/
@SuppressWarnings("unchecked")
@GuardedBy("getAddressCacheFieldOfInetAddress()")
static Map<String, Object> getCacheFiledOfInetAddress$CacheEntry()
static Map<String, Object> getCacheFiledOfAddressCacheFiledOfInetAddress()
throws NoSuchFieldException, IllegalAccessException {
return getCacheFiledOfInetAddress$Cache0(getAddressCacheFieldOfInetAddress());
}

/**
* @return {@link InetAddress.Cache#cache} in {@link InetAddress#negativeCache}
*/
@GuardedBy("getAddressCacheFieldOfInetAddress()")
static Map<String, Object> getCacheFiledOfNegativeCacheFiledOfInetAddress()
throws NoSuchFieldException, IllegalAccessException {
return getCacheFiledOfInetAddress$Cache0(getNegativeCacheFieldOfInetAddress());
}

@SuppressWarnings("unchecked")
static Map<String, Object> getCacheFiledOfInetAddress$Cache0(Object inetAddressCache)
throws NoSuchFieldException, IllegalAccessException {
final Object inetAddressCache = getAddressCacheFieldOfInetAddress();
Class clazz = inetAddressCache.getClass();

final Field cacheMapField = clazz.getDeclaredField("cache");
cacheMapField.setAccessible(true);
return (Map<String, Object>) cacheMapField.get(inetAddressCache);
}

static volatile Object ADDRESS_CACHE = null;

/**
* @return {@link InetAddress#addressCache}
*/
static Object getAddressCacheFieldOfInetAddress()
throws NoSuchFieldException, IllegalAccessException {
if (ADDRESS_CACHE == null) {
return getAddressCacheFieldsOfInetAddress0()[0];
}

/**
* @return {@link InetAddress#negativeCache}
*/
static Object getNegativeCacheFieldOfInetAddress()
throws NoSuchFieldException, IllegalAccessException {
return getAddressCacheFieldsOfInetAddress0()[1];
}

static volatile Object[] ADDRESS_CACHE_AND_NEGATIVE_CACHE = null;

/**
* @return {@link InetAddress#addressCache} and {@link InetAddress#negativeCache}
*/
static Object[] getAddressCacheFieldsOfInetAddress0()
throws NoSuchFieldException, IllegalAccessException {
if (ADDRESS_CACHE_AND_NEGATIVE_CACHE == null) {
synchronized (InetAddressCacheUtil.class) {
if (ADDRESS_CACHE == null) { // double check
if (ADDRESS_CACHE_AND_NEGATIVE_CACHE == null) { // double check
final Field cacheField = InetAddress.class.getDeclaredField("addressCache");
cacheField.setAccessible(true);
ADDRESS_CACHE = cacheField.get(InetAddress.class);

final Field negativeCacheField = InetAddress.class.getDeclaredField("negativeCache");
negativeCacheField.setAccessible(true);

ADDRESS_CACHE_AND_NEGATIVE_CACHE = new Object[]{
cacheField.get(InetAddress.class),
negativeCacheField.get(InetAddress.class)
};
}
}
}
return ADDRESS_CACHE;
return ADDRESS_CACHE_AND_NEGATIVE_CACHE;
}

static InetAddress[] toInetAddressArray(String host, String[] ips) throws UnknownHostException {
Expand Down Expand Up @@ -136,31 +176,39 @@ static byte[] ip2ByteArray(String ip) {
return address;
}

@Nullable
public static DnsCacheEntry getInetAddressCache(String host)
throws NoSuchFieldException, IllegalAccessException {
host = host.toLowerCase();

Object cacheEntry;
synchronized (getAddressCacheFieldOfInetAddress()) {
cacheEntry = getCacheFiledOfInetAddress$CacheEntry().get(host);
cacheEntry = getCacheFiledOfAddressCacheFiledOfInetAddress().get(host);
}
return inetAddress$CacheEntry2DnsCacheEntry(host, cacheEntry);
}

public static List<DnsCacheEntry> listInetAddressCache()
public static DnsCache listInetAddressCache()
throws NoSuchFieldException, IllegalAccessException {
List<DnsCacheEntry> list = new ArrayList<DnsCacheEntry>();

final Map<String, Object> cache;
final Map<String, Object> negativeCache;
synchronized (getAddressCacheFieldOfInetAddress()) {
cache = new HashMap<String, Object>(getCacheFiledOfInetAddress$CacheEntry());
cache = new HashMap<String, Object>(getCacheFiledOfAddressCacheFiledOfInetAddress());
negativeCache = new HashMap<String, Object>(getCacheFiledOfNegativeCacheFiledOfInetAddress());
}

List<DnsCacheEntry> retCache = new ArrayList<DnsCacheEntry>();
for (Map.Entry<String, Object> entry : cache.entrySet()) {
final String host = entry.getKey();
list.add(inetAddress$CacheEntry2DnsCacheEntry(host, entry.getValue()));
retCache.add(inetAddress$CacheEntry2DnsCacheEntry(host, entry.getValue()));
}
List<DnsCacheEntry> retNegativeCache = new ArrayList<DnsCacheEntry>();
for (Map.Entry<String, Object> entry : negativeCache.entrySet()) {
final String host = entry.getKey();
retNegativeCache.add(inetAddress$CacheEntry2DnsCacheEntry(host, entry.getValue()));
}
return list;
return new DnsCache(retCache, retNegativeCache);
}


Expand Down Expand Up @@ -212,7 +260,8 @@ public static List<DnsCacheEntry> listInetAddressCache()

public static void clearInetAddressCache() throws NoSuchFieldException, IllegalAccessException {
synchronized (getAddressCacheFieldOfInetAddress()) {
getCacheFiledOfInetAddress$CacheEntry().clear();
getCacheFiledOfAddressCacheFiledOfInetAddress().clear();
getCacheFiledOfNegativeCacheFiledOfInetAddress().clear();
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/test/java/com/alibaba/dcm/DnsCacheEntryTest.java
Expand Up @@ -63,4 +63,13 @@ public void test_getter() throws Exception {
assertEquals("1.1.1.1", entryIps.getIp());
assertArrayEquals(new String[]{"1.1.1.1", "2.2.2.2"}, entryIps.getIps());
}

@Test
public void test_toString() throws Exception {
final Date expiration = new Date();
DnsCacheEntry entry = new DnsCacheEntry("a.com", new String[]{"1.1.1.1"}, expiration);

String expected = String.format("DnsCacheEntry{host='a.com', ips=[1.1.1.1], expiration=%s}", expiration);
assertEquals(expected, entry.toString());
}
}
50 changes: 50 additions & 0 deletions src/test/java/com/alibaba/dcm/DnsCacheTest.java
@@ -0,0 +1,50 @@
package com.alibaba.dcm;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;

import static org.junit.Assert.*;

/**
* @author Jerry Lee (oldratlee at gmail dot com)
*/
public class DnsCacheTest {
@Test
public void test_equals() throws Exception {
final Date expiration = new Date();
DnsCacheEntry entry1 = new DnsCacheEntry("a.com", new String[]{"1.1.1.1"}, expiration);
DnsCacheEntry entry2 = new DnsCacheEntry("b.com", new String[]{"1.1.1.2"}, expiration);
DnsCacheEntry entry3 = new DnsCacheEntry("c.com", new String[]{"1.1.1.2"}, expiration);
DnsCacheEntry entry4 = new DnsCacheEntry("d.com", new String[]{"1.1.1.2"}, expiration);

DnsCache dnsCache1 = new DnsCache(
Arrays.asList(entry1, entry2),
Arrays.asList(entry3));
DnsCache dnsCache2 = new DnsCache(
Arrays.asList(entry1, entry2),
Arrays.asList(entry4));

assertNotEquals(dnsCache1, dnsCache2);
}

@Test
public void test_toString() throws Exception {
final Date expiration = new Date();
DnsCacheEntry entry1 = new DnsCacheEntry("a.com", new String[]{"1.1.1.1"}, expiration);
DnsCacheEntry entry2 = new DnsCacheEntry("b.com", new String[]{"1.1.1.2"}, expiration);
DnsCacheEntry entry3 = new DnsCacheEntry("c.com", new String[]{"1.1.1.2"}, expiration);

DnsCache dnsCache = new DnsCache(
Arrays.asList(entry1, entry2),
Arrays.asList(entry3));

String expected = String.format("DnsCache{cache=[DnsCacheEntry{host='a.com', ips=[1.1.1.1], expiration=%s}" +
", DnsCacheEntry{host='b.com', ips=[1.1.1.2], expiration=%<s}]" +
", negativeCache=[DnsCacheEntry{host='c.com', ips=[1.1.1.2], expiration=%<s}]}", expiration);

assertEquals(expected, dnsCache.toString());
}
}

0 comments on commit 3cb1c68

Please sign in to comment.