1048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes/* 2048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * Copyright (C) 2010 The Android Open Source Project 3f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes * 4048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * Licensed under the Apache License, Version 2.0 (the "License"); 5048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * you may not use this file except in compliance with the License. 6048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * You may obtain a copy of the License at 7f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes * 8048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * http://www.apache.org/licenses/LICENSE-2.0 9f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes * 10048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * Unless required by applicable law or agreed to in writing, software 11048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * distributed under the License is distributed on an "AS IS" BASIS, 12048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * See the License for the specific language governing permissions and 14048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * limitations under the License. 15048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes */ 16048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes 17048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughespackage java.net; 18048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes 197d95dd2ed60350f90fe64c33cbde58e5c2a72c83Jesse Wilsonimport libcore.util.BasicLruCache; 20048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes 21048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes/** 22048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * Implements caching for {@code InetAddress}. We use a unified cache for both positive and negative 23048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * cache entries. 24afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes * 25afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes * TODO: benchmark and optimize InetAddress until we get to the point where we can just rely on 26afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes * the C library level caching. The main thing caching at this level buys us is avoiding repeated 27afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes * conversions from 'struct sockaddr's to InetAddress[]. 28048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes */ 29048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughesclass AddressCache { 30048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes /** 31048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * When the cache contains more entries than this, we start dropping the oldest ones. 32048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * This should be a power of two to avoid wasted space in our custom map. 33048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes */ 34afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes private static final int MAX_ENTRIES = 16; 35f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 36afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes // The TTL for the Java-level cache is short, just 2s. 37afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes private static final long TTL_NANOS = 2 * 1000000000L; 38f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 39048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes // The actual cache. 403e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen private final BasicLruCache<AddressCacheKey, AddressCacheEntry> cache 413e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen = new BasicLruCache<AddressCacheKey, AddressCacheEntry>(MAX_ENTRIES); 423e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen 433e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen static class AddressCacheKey { 443e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen private final String mHostname; 453e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen private final int mNetId; 463e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen 473e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen AddressCacheKey(String hostname, int netId) { 483e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen mHostname = hostname; 493e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen mNetId = netId; 503e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen } 513e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen 523e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen @Override public boolean equals(Object o) { 533e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen if (this == o) { 543e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen return true; 553e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen } 563e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen if (!(o instanceof AddressCacheKey)) { 573e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen return false; 583e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen } 593e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen AddressCacheKey lhs = (AddressCacheKey) o; 603e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen return mHostname.equals(lhs.mHostname) && mNetId == lhs.mNetId; 613e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen } 623e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen 633e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen @Override public int hashCode() { 643e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen int result = 17; 653e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen result = 31 * result + mNetId; 663e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen result = 31 * result + mHostname.hashCode(); 673e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen return result; 683e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen } 693e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen } 70f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 71e5a4be15160c6f2cf54729ab29de21c0944dfbceElliott Hughes static class AddressCacheEntry { 72fbbae9740d65620b417b85576aa0d6c7daf4ba34Elliott Hughes // Either an InetAddress[] for a positive entry, 73fbbae9740d65620b417b85576aa0d6c7daf4ba34Elliott Hughes // or a String detail message for a negative entry. 74fbbae9740d65620b417b85576aa0d6c7daf4ba34Elliott Hughes final Object value; 75f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 76048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes /** 77048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * The absolute expiry time in nanoseconds. Nanoseconds from System.nanoTime is ideal 78048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * because -- unlike System.currentTimeMillis -- it can never go backwards. 79f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes * 80afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes * We don't need to worry about overflow with a TTL_NANOS of 2s. 81048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes */ 82fbbae9740d65620b417b85576aa0d6c7daf4ba34Elliott Hughes final long expiryNanos; 83f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 84afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes AddressCacheEntry(Object value) { 85fbbae9740d65620b417b85576aa0d6c7daf4ba34Elliott Hughes this.value = value; 86afd70b773bd938c845a3bb0d9a3e21ec64d4db1aElliott Hughes this.expiryNanos = System.nanoTime() + TTL_NANOS; 87048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes } 88048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes } 89f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 90048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes /** 91b744a7edf23c14216698ad69ea59151e07cc50b8Elliott Hughes * Removes all entries from the cache. 92b744a7edf23c14216698ad69ea59151e07cc50b8Elliott Hughes */ 93b744a7edf23c14216698ad69ea59151e07cc50b8Elliott Hughes public void clear() { 947d95dd2ed60350f90fe64c33cbde58e5c2a72c83Jesse Wilson cache.evictAll(); 95b744a7edf23c14216698ad69ea59151e07cc50b8Elliott Hughes } 96b744a7edf23c14216698ad69ea59151e07cc50b8Elliott Hughes 97b744a7edf23c14216698ad69ea59151e07cc50b8Elliott Hughes /** 983e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen * Returns the cached InetAddress[] for 'hostname' on network 'netId'. Returns null 993e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen * if nothing is known about 'hostname'. Returns a String suitable for use as an 1003e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen * UnknownHostException detail message if 'hostname' is known not to exist. 101048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes */ 1023e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen public Object get(String hostname, int netId) { 1033e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen AddressCacheEntry entry = cache.get(new AddressCacheKey(hostname, netId)); 104048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes // Do we have a valid cache entry? 105048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes if (entry != null && entry.expiryNanos >= System.nanoTime()) { 106fbbae9740d65620b417b85576aa0d6c7daf4ba34Elliott Hughes return entry.value; 107048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes } 108048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes // Either we didn't find anything, or it had expired. 109048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes // No need to remove expired entries: the caller will provide a replacement shortly. 110048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes return null; 111048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes } 112f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 113048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes /** 114048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * Associates the given 'addresses' with 'hostname'. The association will expire after a 115048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * certain length of time. 116048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes */ 1173e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen public void put(String hostname, int netId, InetAddress[] addresses) { 1183e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(addresses)); 119048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes } 120f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 121048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes /** 122048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * Records that 'hostname' is known not to have any associated addresses. (I.e. insert a 123048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes * negative cache entry.) 124048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes */ 1253e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen public void putUnknownHost(String hostname, int netId, String detailMessage) { 1263e58734d651080009c9190c7062837fca5c7cf4ePaul Jensen cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(detailMessage)); 127048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes } 128048303b64df9c987ae2f57b6bf88ff5ac1b5cca0Elliott Hughes} 129