Home | History | Annotate | Download | only in impl
      1 // Copyright 2003-2005 Arthur van Hoff Rick Blair
      2 // Licensed under Apache License version 2.0
      3 // Original license LGPL
      4 
      5 package javax.jmdns.impl;
      6 
      7 import java.util.AbstractMap;
      8 import java.util.ArrayList;
      9 import java.util.Collection;
     10 import java.util.Collections;
     11 import java.util.HashSet;
     12 import java.util.Iterator;
     13 import java.util.List;
     14 import java.util.Map;
     15 import java.util.Set;
     16 
     17 import javax.jmdns.impl.constants.DNSRecordClass;
     18 import javax.jmdns.impl.constants.DNSRecordType;
     19 
     20 /**
     21  * A table of DNS entries. This is a map table which can handle multiple entries with the same name.
     22  * <p/>
     23  * Storing multiple entries with the same name is implemented using a linked list. This is hidden from the user and can change in later implementation.
     24  * <p/>
     25  * Here's how to iterate over all entries:
     26  *
     27  * <pre>
     28  *       for (Iterator i=dnscache.allValues().iterator(); i.hasNext(); ) {
     29  *             DNSEntry entry = i.next();
     30  *             ...do something with entry...
     31  *       }
     32  * </pre>
     33  * <p/>
     34  * And here's how to iterate over all entries having a given name:
     35  *
     36  * <pre>
     37  *       for (Iterator i=dnscache.getDNSEntryList(name).iterator(); i.hasNext(); ) {
     38  *             DNSEntry entry = i.next();
     39  *           ...do something with entry...
     40  *       }
     41  * </pre>
     42  *
     43  * @author Arthur van Hoff, Werner Randelshofer, Rick Blair, Pierre Frisch
     44  */
     45 public class DNSCache extends AbstractMap<String, List<? extends DNSEntry>> {
     46 
     47     // private static Logger logger = Logger.getLogger(DNSCache.class.getName());
     48 
     49     private transient Set<Map.Entry<String, List<? extends DNSEntry>>> _entrySet  = null;
     50 
     51     /**
     52      *
     53      */
     54     public static final DNSCache                                       EmptyCache = new _EmptyCache();
     55 
     56     static final class _EmptyCache extends DNSCache {
     57 
     58         /**
     59          * {@inheritDoc}
     60          */
     61         @Override
     62         public int size() {
     63             return 0;
     64         }
     65 
     66         /**
     67          * {@inheritDoc}
     68          */
     69         @Override
     70         public boolean isEmpty() {
     71             return true;
     72         }
     73 
     74         /**
     75          * {@inheritDoc}
     76          */
     77         @Override
     78         public boolean containsKey(Object key) {
     79             return false;
     80         }
     81 
     82         /**
     83          * {@inheritDoc}
     84          */
     85         @Override
     86         public boolean containsValue(Object value) {
     87             return false;
     88         }
     89 
     90         /**
     91          * {@inheritDoc}
     92          */
     93         @Override
     94         public List<DNSEntry> get(Object key) {
     95             return null;
     96         }
     97 
     98         /**
     99          * {@inheritDoc}
    100          */
    101         @Override
    102         public Set<String> keySet() {
    103             return Collections.emptySet();
    104         }
    105 
    106         /**
    107          * {@inheritDoc}
    108          */
    109         @Override
    110         public Collection<List<? extends DNSEntry>> values() {
    111             return Collections.emptySet();
    112         }
    113 
    114         /**
    115          * {@inheritDoc}
    116          */
    117         @Override
    118         public Set<Map.Entry<String, List<? extends DNSEntry>>> entrySet() {
    119             return Collections.emptySet();
    120         }
    121 
    122         /**
    123          * {@inheritDoc}
    124          */
    125         @Override
    126         public boolean equals(Object o) {
    127             return (o instanceof Map) && ((Map<?, ?>) o).size() == 0;
    128         }
    129 
    130         /**
    131          * {@inheritDoc}
    132          */
    133         @Override
    134         public List<? extends DNSEntry> put(String key, List<? extends DNSEntry> value) {
    135             return null;
    136         }
    137 
    138         /**
    139          * {@inheritDoc}
    140          */
    141         @Override
    142         public int hashCode() {
    143             return 0;
    144         }
    145 
    146     }
    147 
    148     /**
    149      *
    150      */
    151     protected static class _CacheEntry extends Object implements Map.Entry<String, List<? extends DNSEntry>> {
    152 
    153         private List<? extends DNSEntry> _value;
    154 
    155         private String                   _key;
    156 
    157         /**
    158          * @param key
    159          * @param value
    160          */
    161         protected _CacheEntry(String key, List<? extends DNSEntry> value) {
    162             super();
    163             _key = (key != null ? key.trim().toLowerCase() : null);
    164             _value = value;
    165         }
    166 
    167         /**
    168          * @param entry
    169          */
    170         protected _CacheEntry(Map.Entry<String, List<? extends DNSEntry>> entry) {
    171             super();
    172             if (entry instanceof _CacheEntry) {
    173                 _key = ((_CacheEntry) entry).getKey();
    174                 _value = ((_CacheEntry) entry).getValue();
    175             }
    176         }
    177 
    178         /**
    179          * {@inheritDoc}
    180          */
    181         @Override
    182         public String getKey() {
    183             return (_key != null ? _key : "");
    184         }
    185 
    186         /**
    187          * {@inheritDoc}
    188          */
    189         @Override
    190         public List<? extends DNSEntry> getValue() {
    191             return _value;
    192         }
    193 
    194         /**
    195          * {@inheritDoc}
    196          */
    197         @Override
    198         public List<? extends DNSEntry> setValue(List<? extends DNSEntry> value) {
    199             List<? extends DNSEntry> oldValue = _value;
    200             _value = value;
    201             return oldValue;
    202         }
    203 
    204         /**
    205          * Returns <tt>true</tt> if this list contains no elements.
    206          *
    207          * @return <tt>true</tt> if this list contains no elements
    208          */
    209         public boolean isEmpty() {
    210             return this.getValue().isEmpty();
    211         }
    212 
    213         /**
    214          * {@inheritDoc}
    215          */
    216         @Override
    217         public boolean equals(Object entry) {
    218             if (!(entry instanceof Map.Entry)) {
    219                 return false;
    220             }
    221             return this.getKey().equals(((Map.Entry<?, ?>) entry).getKey()) && this.getValue().equals(((Map.Entry<?, ?>) entry).getValue());
    222         }
    223 
    224         /**
    225          * {@inheritDoc}
    226          */
    227         @Override
    228         public int hashCode() {
    229             return (_key == null ? 0 : _key.hashCode());
    230         }
    231 
    232         /**
    233          * {@inheritDoc}
    234          */
    235         @Override
    236         public synchronized String toString() {
    237             StringBuffer aLog = new StringBuffer(200);
    238             aLog.append("\n\t\tname '");
    239             aLog.append(_key);
    240             aLog.append("' ");
    241             if ((_value != null) && (!_value.isEmpty())) {
    242                 for (DNSEntry entry : _value) {
    243                     aLog.append("\n\t\t\t");
    244                     aLog.append(entry.toString());
    245                 }
    246             } else {
    247                 aLog.append(" no entries");
    248             }
    249             return aLog.toString();
    250         }
    251     }
    252 
    253     /**
    254      *
    255      */
    256     public DNSCache() {
    257         this(1024);
    258     }
    259 
    260     /**
    261      * @param map
    262      */
    263     public DNSCache(DNSCache map) {
    264         this(map != null ? map.size() : 1024);
    265         if (map != null) {
    266             this.putAll(map);
    267         }
    268     }
    269 
    270     /**
    271      * Create a table with a given initial size.
    272      *
    273      * @param initialCapacity
    274      */
    275     public DNSCache(int initialCapacity) {
    276         super();
    277         _entrySet = new HashSet<Map.Entry<String, List<? extends DNSEntry>>>(initialCapacity);
    278     }
    279 
    280     // ====================================================================
    281     // Map
    282 
    283     /*
    284      * (non-Javadoc)
    285      * @see java.util.AbstractMap#entrySet()
    286      */
    287     @Override
    288     public Set<Map.Entry<String, List<? extends DNSEntry>>> entrySet() {
    289         if (_entrySet == null) {
    290             _entrySet = new HashSet<Map.Entry<String, List<? extends DNSEntry>>>();
    291         }
    292         return _entrySet;
    293     }
    294 
    295     /**
    296      * @param key
    297      * @return map entry for the key
    298      */
    299     protected Map.Entry<String, List<? extends DNSEntry>> getEntry(String key) {
    300         String stringKey = (key != null ? key.trim().toLowerCase() : null);
    301         for (Map.Entry<String, List<? extends DNSEntry>> entry : this.entrySet()) {
    302             if (stringKey != null) {
    303                 if (stringKey.equals(entry.getKey())) {
    304                     return entry;
    305                 }
    306             } else {
    307                 if (entry.getKey() == null) {
    308                     return entry;
    309                 }
    310             }
    311         }
    312         return null;
    313     }
    314 
    315     /**
    316      * {@inheritDoc}
    317      */
    318     @Override
    319     public List<? extends DNSEntry> put(String key, List<? extends DNSEntry> value) {
    320         synchronized (this) {
    321             List<? extends DNSEntry> oldValue = null;
    322             Map.Entry<String, List<? extends DNSEntry>> oldEntry = this.getEntry(key);
    323             if (oldEntry != null) {
    324                 oldValue = oldEntry.setValue(value);
    325             } else {
    326                 this.entrySet().add(new _CacheEntry(key, value));
    327             }
    328             return oldValue;
    329         }
    330     }
    331 
    332     /**
    333      * {@inheritDoc}
    334      */
    335     @Override
    336     protected Object clone() throws CloneNotSupportedException {
    337         return new DNSCache(this);
    338     }
    339 
    340     // ====================================================================
    341 
    342     /**
    343      * Returns all entries in the cache
    344      *
    345      * @return all entries in the cache
    346      */
    347     public synchronized Collection<DNSEntry> allValues() {
    348         List<DNSEntry> allValues = new ArrayList<DNSEntry>();
    349         for (List<? extends DNSEntry> entry : this.values()) {
    350             if (entry != null) {
    351                 allValues.addAll(entry);
    352             }
    353         }
    354         return allValues;
    355     }
    356 
    357     /**
    358      * Iterate only over items with matching name. Returns an list of DNSEntry or null. To retrieve all entries, one must iterate over this linked list.
    359      *
    360      * @param name
    361      * @return list of DNSEntries
    362      */
    363     public synchronized Collection<? extends DNSEntry> getDNSEntryList(String name) {
    364         Collection<? extends DNSEntry> entryList = this._getDNSEntryList(name);
    365         if (entryList != null) {
    366             entryList = new ArrayList<DNSEntry>(entryList);
    367         } else {
    368             entryList = Collections.emptyList();
    369         }
    370         return entryList;
    371     }
    372 
    373     private Collection<? extends DNSEntry> _getDNSEntryList(String name) {
    374         return this.get(name != null ? name.toLowerCase() : null);
    375     }
    376 
    377     /**
    378      * Get a matching DNS entry from the table (using isSameEntry). Returns the entry that was found.
    379      *
    380      * @param dnsEntry
    381      * @return DNSEntry
    382      */
    383     public synchronized DNSEntry getDNSEntry(DNSEntry dnsEntry) {
    384         DNSEntry result = null;
    385         if (dnsEntry != null) {
    386             Collection<? extends DNSEntry> entryList = this._getDNSEntryList(dnsEntry.getKey());
    387             if (entryList != null) {
    388                 for (DNSEntry testDNSEntry : entryList) {
    389                     if (testDNSEntry.isSameEntry(dnsEntry)) {
    390                         result = testDNSEntry;
    391                         break;
    392                     }
    393                 }
    394             }
    395         }
    396         return result;
    397     }
    398 
    399     /**
    400      * Get a matching DNS entry from the table.
    401      *
    402      * @param name
    403      * @param type
    404      * @param recordClass
    405      * @return DNSEntry
    406      */
    407     public synchronized DNSEntry getDNSEntry(String name, DNSRecordType type, DNSRecordClass recordClass) {
    408         DNSEntry result = null;
    409         Collection<? extends DNSEntry> entryList = this._getDNSEntryList(name);
    410         if (entryList != null) {
    411             for (DNSEntry testDNSEntry : entryList) {
    412                 if (testDNSEntry.getRecordType().equals(type) && ((DNSRecordClass.CLASS_ANY == recordClass) || testDNSEntry.getRecordClass().equals(recordClass))) {
    413                     result = testDNSEntry;
    414                     break;
    415                 }
    416             }
    417         }
    418         return result;
    419     }
    420 
    421     /**
    422      * Get all matching DNS entries from the table.
    423      *
    424      * @param name
    425      * @param type
    426      * @param recordClass
    427      * @return list of entries
    428      */
    429     public synchronized Collection<? extends DNSEntry> getDNSEntryList(String name, DNSRecordType type, DNSRecordClass recordClass) {
    430         Collection<? extends DNSEntry> entryList = this._getDNSEntryList(name);
    431         if (entryList != null) {
    432             entryList = new ArrayList<DNSEntry>(entryList);
    433             for (Iterator<? extends DNSEntry> i = entryList.iterator(); i.hasNext();) {
    434                 DNSEntry testDNSEntry = i.next();
    435                 if (!testDNSEntry.getRecordType().equals(type) || ((DNSRecordClass.CLASS_ANY != recordClass) && !testDNSEntry.getRecordClass().equals(recordClass))) {
    436                     i.remove();
    437                 }
    438             }
    439         } else {
    440             entryList = Collections.emptyList();
    441         }
    442         return entryList;
    443     }
    444 
    445     /**
    446      * Adds an entry to the table.
    447      *
    448      * @param dnsEntry
    449      * @return true if the entry was added
    450      */
    451     public synchronized boolean addDNSEntry(final DNSEntry dnsEntry) {
    452         boolean result = false;
    453         if (dnsEntry != null) {
    454             Map.Entry<String, List<? extends DNSEntry>> oldEntry = this.getEntry(dnsEntry.getKey());
    455 
    456             List<DNSEntry> aNewValue = null;
    457             if (oldEntry != null) {
    458                 aNewValue = new ArrayList<DNSEntry>(oldEntry.getValue());
    459             } else {
    460                 aNewValue = new ArrayList<DNSEntry>();
    461             }
    462             aNewValue.add(dnsEntry);
    463 
    464             if (oldEntry != null) {
    465                 oldEntry.setValue(aNewValue);
    466             } else {
    467                 this.entrySet().add(new _CacheEntry(dnsEntry.getKey(), aNewValue));
    468             }
    469             // This is probably not very informative
    470             result = true;
    471         }
    472         return result;
    473     }
    474 
    475     /**
    476      * Removes a specific entry from the table. Returns true if the entry was found.
    477      *
    478      * @param dnsEntry
    479      * @return true if the entry was removed
    480      */
    481     public synchronized boolean removeDNSEntry(DNSEntry dnsEntry) {
    482         boolean result = false;
    483         if (dnsEntry != null) {
    484             Map.Entry<String, List<? extends DNSEntry>> existingEntry = this.getEntry(dnsEntry.getKey());
    485             if (existingEntry != null) {
    486                 result = existingEntry.getValue().remove(dnsEntry);
    487                 // If we just removed the last one we need to get rid of the entry
    488                 if (existingEntry.getValue().isEmpty()) {
    489                     this.entrySet().remove(existingEntry);
    490                 }
    491             }
    492         }
    493         return result;
    494     }
    495 
    496     /**
    497      * Replace an existing entry by a new one.<br/>
    498      * <b>Note:</b> the 2 entries must have the same key.
    499      *
    500      * @param newDNSEntry
    501      * @param existingDNSEntry
    502      * @return <code>true</code> if the entry has been replace, <code>false</code> otherwise.
    503      */
    504     public synchronized boolean replaceDNSEntry(DNSEntry newDNSEntry, DNSEntry existingDNSEntry) {
    505         boolean result = false;
    506         if ((newDNSEntry != null) && (existingDNSEntry != null) && (newDNSEntry.getKey().equals(existingDNSEntry.getKey()))) {
    507             Map.Entry<String, List<? extends DNSEntry>> oldEntry = this.getEntry(newDNSEntry.getKey());
    508 
    509             List<DNSEntry> aNewValue = null;
    510             if (oldEntry != null) {
    511                 aNewValue = new ArrayList<DNSEntry>(oldEntry.getValue());
    512             } else {
    513                 aNewValue = new ArrayList<DNSEntry>();
    514             }
    515             aNewValue.remove(existingDNSEntry);
    516             aNewValue.add(newDNSEntry);
    517 
    518             if (oldEntry != null) {
    519                 oldEntry.setValue(aNewValue);
    520             } else {
    521                 this.entrySet().add(new _CacheEntry(newDNSEntry.getKey(), aNewValue));
    522             }
    523             // This is probably not very informative
    524             result = true;
    525         }
    526         return result;
    527     }
    528 
    529     /**
    530      * {@inheritDoc}
    531      */
    532     @Override
    533     public synchronized String toString() {
    534         StringBuffer aLog = new StringBuffer(2000);
    535         aLog.append("\t---- cache ----");
    536         for (Map.Entry<String, List<? extends DNSEntry>> entry : this.entrySet()) {
    537             aLog.append("\n\t\t");
    538             aLog.append(entry.toString());
    539         }
    540         return aLog.toString();
    541     }
    542 
    543 }
    544