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