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.List;
022    import java.util.Map;
023    import java.util.concurrent.ConcurrentHashMap;
024    
025    import org.apache.hadoop.classification.InterfaceAudience;
026    import org.apache.hadoop.classification.InterfaceStability;
027    
028    /**
029     * A cached implementation of DNSToSwitchMapping that takes an
030     * raw DNSToSwitchMapping and stores the resolved network location in 
031     * a cache. The following calls to a resolved network location
032     * will get its location from the cache. 
033     *
034     */
035    @InterfaceAudience.Public
036    @InterfaceStability.Evolving
037    public class CachedDNSToSwitchMapping extends AbstractDNSToSwitchMapping {
038      private Map<String, String> cache = new ConcurrentHashMap<String, String>();
039    
040      /**
041       * The uncached mapping
042       */
043      protected final DNSToSwitchMapping rawMapping;
044    
045      /**
046       * cache a raw DNS mapping
047       * @param rawMapping the raw mapping to cache
048       */
049      public CachedDNSToSwitchMapping(DNSToSwitchMapping rawMapping) {
050        this.rawMapping = rawMapping;
051      }
052    
053      /**
054       * @param names a list of hostnames to probe for being cached
055       * @return the hosts from 'names' that have not been cached previously
056       */
057      private List<String> getUncachedHosts(List<String> names) {
058        // find out all names without cached resolved location
059        List<String> unCachedHosts = new ArrayList<String>(names.size());
060        for (String name : names) {
061          if (cache.get(name) == null) {
062            unCachedHosts.add(name);
063          } 
064        }
065        return unCachedHosts;
066      }
067    
068      /**
069       * Caches the resolved host:rack mappings. The two list
070       * parameters must be of equal size.
071       *
072       * @param uncachedHosts a list of hosts that were uncached
073       * @param resolvedHosts a list of resolved host entries where the element
074       * at index(i) is the resolved value for the entry in uncachedHosts[i]
075       */
076      private void cacheResolvedHosts(List<String> uncachedHosts, 
077          List<String> resolvedHosts) {
078        // Cache the result
079        if (resolvedHosts != null) {
080          for (int i=0; i<uncachedHosts.size(); i++) {
081            cache.put(uncachedHosts.get(i), resolvedHosts.get(i));
082          }
083        }
084      }
085    
086      /**
087       * @param names a list of hostnames to look up (can be be empty)
088       * @return the cached resolution of the list of hostnames/addresses.
089       *  or null if any of the names are not currently in the cache
090       */
091      private List<String> getCachedHosts(List<String> names) {
092        List<String> result = new ArrayList<String>(names.size());
093        // Construct the result
094        for (String name : names) {
095          String networkLocation = cache.get(name);
096          if (networkLocation != null) {
097            result.add(networkLocation);
098          } else {
099            return null;
100          }
101        }
102        return result;
103      }
104    
105      @Override
106      public List<String> resolve(List<String> names) {
107        // normalize all input names to be in the form of IP addresses
108        names = NetUtils.normalizeHostNames(names);
109    
110        List <String> result = new ArrayList<String>(names.size());
111        if (names.isEmpty()) {
112          return result;
113        }
114    
115        List<String> uncachedHosts = getUncachedHosts(names);
116    
117        // Resolve the uncached hosts
118        List<String> resolvedHosts = rawMapping.resolve(uncachedHosts);
119        //cache them
120        cacheResolvedHosts(uncachedHosts, resolvedHosts);
121        //now look up the entire list in the cache
122        return getCachedHosts(names);
123    
124      }
125    
126      /**
127       * Delegate the switch topology query to the raw mapping, via
128       * {@link AbstractDNSToSwitchMapping#isMappingSingleSwitch(DNSToSwitchMapping)}
129       * @return true iff the raw mapper is considered single-switch.
130       */
131      @Override
132      public boolean isSingleSwitch() {
133        return isMappingSingleSwitch(rawMapping);
134      }
135    }