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 */
018package org.apache.hadoop.net;
019
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.concurrent.ConcurrentHashMap;
025
026import org.apache.hadoop.classification.InterfaceAudience;
027import 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
038public 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}