Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package java.net;
     18 
     19 import libcore.util.BasicLruCache;
     20 
     21 /**
     22  * Implements caching for {@code InetAddress}. We use a unified cache for both positive and negative
     23  * cache entries.
     24  *
     25  * TODO: benchmark and optimize InetAddress until we get to the point where we can just rely on
     26  * the C library level caching. The main thing caching at this level buys us is avoiding repeated
     27  * conversions from 'struct sockaddr's to InetAddress[].
     28  */
     29 class AddressCache {
     30     /**
     31      * When the cache contains more entries than this, we start dropping the oldest ones.
     32      * This should be a power of two to avoid wasted space in our custom map.
     33      */
     34     private static final int MAX_ENTRIES = 16;
     35 
     36     // The TTL for the Java-level cache is short, just 2s.
     37     private static final long TTL_NANOS = 2 * 1000000000L;
     38 
     39     // The actual cache.
     40     private final BasicLruCache<AddressCacheKey, AddressCacheEntry> cache
     41             = new BasicLruCache<AddressCacheKey, AddressCacheEntry>(MAX_ENTRIES);
     42 
     43     static class AddressCacheKey {
     44         private final String mHostname;
     45         private final int mNetId;
     46 
     47         AddressCacheKey(String hostname, int netId) {
     48             mHostname = hostname;
     49             mNetId = netId;
     50         }
     51 
     52         @Override public boolean equals(Object o) {
     53             if (this == o) {
     54                 return true;
     55             }
     56             if (!(o instanceof AddressCacheKey)) {
     57                 return false;
     58             }
     59             AddressCacheKey lhs = (AddressCacheKey) o;
     60             return mHostname.equals(lhs.mHostname) && mNetId == lhs.mNetId;
     61         }
     62 
     63         @Override public int hashCode() {
     64             int result = 17;
     65             result = 31 * result + mNetId;
     66             result = 31 * result + mHostname.hashCode();
     67             return result;
     68         }
     69     }
     70 
     71     static class AddressCacheEntry {
     72         // Either an InetAddress[] for a positive entry,
     73         // or a String detail message for a negative entry.
     74         final Object value;
     75 
     76         /**
     77          * The absolute expiry time in nanoseconds. Nanoseconds from System.nanoTime is ideal
     78          * because -- unlike System.currentTimeMillis -- it can never go backwards.
     79          *
     80          * We don't need to worry about overflow with a TTL_NANOS of 2s.
     81          */
     82         final long expiryNanos;
     83 
     84         AddressCacheEntry(Object value) {
     85             this.value = value;
     86             this.expiryNanos = System.nanoTime() + TTL_NANOS;
     87         }
     88     }
     89 
     90     /**
     91      * Removes all entries from the cache.
     92      */
     93     public void clear() {
     94         cache.evictAll();
     95     }
     96 
     97     /**
     98      * Returns the cached InetAddress[] for 'hostname' on network 'netId'. Returns null
     99      * if nothing is known about 'hostname'. Returns a String suitable for use as an
    100      * UnknownHostException detail message if 'hostname' is known not to exist.
    101      */
    102     public Object get(String hostname, int netId) {
    103         AddressCacheEntry entry = cache.get(new AddressCacheKey(hostname, netId));
    104         // Do we have a valid cache entry?
    105         if (entry != null && entry.expiryNanos >= System.nanoTime()) {
    106             return entry.value;
    107         }
    108         // Either we didn't find anything, or it had expired.
    109         // No need to remove expired entries: the caller will provide a replacement shortly.
    110         return null;
    111     }
    112 
    113     /**
    114      * Associates the given 'addresses' with 'hostname'. The association will expire after a
    115      * certain length of time.
    116      */
    117     public void put(String hostname, int netId, InetAddress[] addresses) {
    118         cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(addresses));
    119     }
    120 
    121     /**
    122      * Records that 'hostname' is known not to have any associated addresses. (I.e. insert a
    123      * negative cache entry.)
    124      */
    125     public void putUnknownHost(String hostname, int netId, String detailMessage) {
    126         cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(detailMessage));
    127     }
    128 }
    129