001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.net;
019    
020    import java.util.ArrayList;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.concurrent.ConcurrentHashMap;
025    
026    import org.apache.hadoop.classification.InterfaceAudience;
027    import org.apache.hadoop.classification.InterfaceStability;
028    
029    /**
030     * A cached implementation of DNSToSwitchMapping that takes an
031     * raw DNSToSwitchMapping and stores the resolved network location in 
032     * a cache. The following calls to a resolved network location
033     * will get its location from the cache. 
034     *
035     */
036    @InterfaceAudience.Public
037    @InterfaceStability.Evolving
038    public class CachedDNSToSwitchMapping extends AbstractDNSToSwitchMapping {
039      private Map<String, String> cache = new ConcurrentHashMap<String, String>();
040    
041      /**
042       * The uncached mapping
043       */
044      protected final DNSToSwitchMapping rawMapping;
045    
046      /**
047       * cache a raw DNS mapping
048       * @param rawMapping the raw mapping to cache
049       */
050      public CachedDNSToSwitchMapping(DNSToSwitchMapping rawMapping) {
051        this.rawMapping = rawMapping;
052      }
053    
054      /**
055       * @param names a list of hostnames to probe for being cached
056       * @return the hosts from 'names' that have not been cached previously
057       */
058      private List<String> getUncachedHosts(List<String> names) {
059        // find out all names without cached resolved location
060        List<String> unCachedHosts = new ArrayList<String>(names.size());
061        for (String name : names) {
062          if (cache.get(name) == null) {
063            unCachedHosts.add(name);
064          } 
065        }
066        return unCachedHosts;
067      }
068    
069      /**
070       * Caches the resolved host:rack mappings. The two list
071       * parameters must be of equal size.
072       *
073       * @param uncachedHosts a list of hosts that were uncached
074       * @param resolvedHosts a list of resolved host entries where the element
075       * at index(i) is the resolved value for the entry in uncachedHosts[i]
076       */
077      private void cacheResolvedHosts(List<String> uncachedHosts, 
078          List<String> resolvedHosts) {
079        // Cache the result
080        if (resolvedHosts != null) {
081          for (int i=0; i<uncachedHosts.size(); i++) {
082            cache.put(uncachedHosts.get(i), resolvedHosts.get(i));
083          }
084        }
085      }
086    
087      /**
088       * @param names a list of hostnames to look up (can be be empty)
089       * @return the cached resolution of the list of hostnames/addresses.
090       *  or null if any of the names are not currently in the cache
091       */
092      private List<String> getCachedHosts(List<String> names) {
093        List<String> result = new ArrayList<String>(names.size());
094        // Construct the result
095        for (String name : names) {
096          String networkLocation = cache.get(name);
097          if (networkLocation != null) {
098            result.add(networkLocation);
099          } else {
100            return null;
101          }
102        }
103        return result;
104      }
105    
106      @Override
107      public List<String> resolve(List<String> names) {
108        // normalize all input names to be in the form of IP addresses
109        names = NetUtils.normalizeHostNames(names);
110    
111        List <String> result = new ArrayList<String>(names.size());
112        if (names.isEmpty()) {
113          return result;
114        }
115    
116        List<String> uncachedHosts = getUncachedHosts(names);
117    
118        // Resolve the uncached hosts
119        List<String> resolvedHosts = rawMapping.resolve(uncachedHosts);
120        //cache them
121        cacheResolvedHosts(uncachedHosts, resolvedHosts);
122        //now look up the entire list in the cache
123        return getCachedHosts(names);
124    
125      }
126    
127      /**
128       * Get the (host x switch) map.
129       * @return a copy of the cached map of hosts to rack
130       */
131      @Override
132      public Map<String, String> getSwitchMap() {
133        Map<String, String > switchMap = new HashMap<String, String>(cache);
134        return switchMap;
135      }
136    
137    
138      @Override
139      public String toString() {
140        return "cached switch mapping relaying to " + rawMapping;
141      }
142    
143      /**
144       * Delegate the switch topology query to the raw mapping, via
145       * {@link AbstractDNSToSwitchMapping#isMappingSingleSwitch(DNSToSwitchMapping)}
146       * @return true iff the raw mapper is considered single-switch.
147       */
148      @Override
149      public boolean isSingleSwitch() {
150        return isMappingSingleSwitch(rawMapping);
151      }
152      
153      @Override
154      public void reloadCachedMappings() {
155        cache.clear();
156      }
157    
158      @Override
159      public void reloadCachedMappings(List<String> names) {
160        for (String name : names) {
161          cache.remove(name);
162        }
163      }
164    }