PageRenderTime 2ms CodeModel.GetById 3ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/hadoop-common/src/java/org/apache/hadoop/net/NetUtils.java

https://bitbucket.org/hadoop/hadoop-0.21.0
Java | 463 lines | 198 code | 38 blank | 227 comment | 44 complexity | 03692c0e7f24fefd9aca59e9a8de6402 MD5 | raw file
  1/**
  2 * Licensed to the Apache Software Foundation (ASF) under one
  3 * or more contributor license agreements.  See the NOTICE file
  4 * distributed with this work for additional information
  5 * regarding copyright ownership.  The ASF licenses this file
  6 * to you under the Apache License, Version 2.0 (the
  7 * "License"); you may not use this file except in compliance
  8 * with the License.  You may obtain a copy of the License at
  9 *
 10 *     http://www.apache.org/licenses/LICENSE-2.0
 11 *
 12 * Unless required by applicable law or agreed to in writing, software
 13 * distributed under the License is distributed on an "AS IS" BASIS,
 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15 * See the License for the specific language governing permissions and
 16 * limitations under the License.
 17 */
 18package org.apache.hadoop.net;
 19
 20import java.io.IOException;
 21import java.io.InputStream;
 22import java.io.OutputStream;
 23import java.net.InetAddress;
 24import java.net.InetSocketAddress;
 25import java.net.Socket;
 26import java.net.SocketAddress;
 27import java.net.URI;
 28import java.net.UnknownHostException;
 29import java.net.ConnectException;
 30import java.nio.channels.SocketChannel;
 31import java.util.Map.Entry;
 32import java.util.regex.Pattern;
 33import java.util.*;
 34
 35import javax.net.SocketFactory;
 36
 37import org.apache.commons.logging.Log;
 38import org.apache.commons.logging.LogFactory;
 39import org.apache.hadoop.classification.InterfaceAudience;
 40import org.apache.hadoop.classification.InterfaceStability;
 41import org.apache.hadoop.conf.Configuration;
 42import org.apache.hadoop.fs.Path;
 43import org.apache.hadoop.ipc.Server;
 44import org.apache.hadoop.ipc.VersionedProtocol;
 45import org.apache.hadoop.util.ReflectionUtils;
 46
 47@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
 48@InterfaceStability.Unstable
 49public class NetUtils {
 50  private static final Log LOG = LogFactory.getLog(NetUtils.class);
 51  
 52  private static Map<String, String> hostToResolved = 
 53                                     new HashMap<String, String>();
 54
 55  /**
 56   * Get the socket factory for the given class according to its
 57   * configuration parameter
 58   * <tt>hadoop.rpc.socket.factory.class.&lt;ClassName&gt;</tt>. When no
 59   * such parameter exists then fall back on the default socket factory as
 60   * configured by <tt>hadoop.rpc.socket.factory.class.default</tt>. If
 61   * this default socket factory is not configured, then fall back on the JVM
 62   * default socket factory.
 63   * 
 64   * @param conf the configuration
 65   * @param clazz the class (usually a {@link VersionedProtocol})
 66   * @return a socket factory
 67   */
 68  public static SocketFactory getSocketFactory(Configuration conf,
 69      Class<?> clazz) {
 70
 71    SocketFactory factory = null;
 72
 73    String propValue =
 74        conf.get("hadoop.rpc.socket.factory.class." + clazz.getSimpleName());
 75    if ((propValue != null) && (propValue.length() > 0))
 76      factory = getSocketFactoryFromProperty(conf, propValue);
 77
 78    if (factory == null)
 79      factory = getDefaultSocketFactory(conf);
 80
 81    return factory;
 82  }
 83
 84  /**
 85   * Get the default socket factory as specified by the configuration
 86   * parameter <tt>hadoop.rpc.socket.factory.default</tt>
 87   * 
 88   * @param conf the configuration
 89   * @return the default socket factory as specified in the configuration or
 90   *         the JVM default socket factory if the configuration does not
 91   *         contain a default socket factory property.
 92   */
 93  public static SocketFactory getDefaultSocketFactory(Configuration conf) {
 94
 95    String propValue = conf.get("hadoop.rpc.socket.factory.class.default");
 96    if ((propValue == null) || (propValue.length() == 0))
 97      return SocketFactory.getDefault();
 98
 99    return getSocketFactoryFromProperty(conf, propValue);
100  }
101
102  /**
103   * Get the socket factory corresponding to the given proxy URI. If the
104   * given proxy URI corresponds to an absence of configuration parameter,
105   * returns null. If the URI is malformed raises an exception.
106   * 
107   * @param propValue the property which is the class name of the
108   *        SocketFactory to instantiate; assumed non null and non empty.
109   * @return a socket factory as defined in the property value.
110   */
111  public static SocketFactory getSocketFactoryFromProperty(
112      Configuration conf, String propValue) {
113
114    try {
115      Class<?> theClass = conf.getClassByName(propValue);
116      return (SocketFactory) ReflectionUtils.newInstance(theClass, conf);
117
118    } catch (ClassNotFoundException cnfe) {
119      throw new RuntimeException("Socket Factory class not found: " + cnfe);
120    }
121  }
122
123  /**
124   * Util method to build socket addr from either:
125   *   <host>:<post>
126   *   <fs>://<host>:<port>/<path>
127   */
128  public static InetSocketAddress createSocketAddr(String target) {
129    return createSocketAddr(target, -1);
130  }
131
132  /**
133   * Util method to build socket addr from either:
134   *   <host>
135   *   <host>:<post>
136   *   <fs>://<host>:<port>/<path>
137   */
138  public static InetSocketAddress createSocketAddr(String target,
139                                                   int defaultPort) {
140    if (target == null) {
141      throw new IllegalArgumentException("Target address cannot be null.");
142    }
143    int colonIndex = target.indexOf(':');
144    if (colonIndex < 0 && defaultPort == -1) {
145      throw new RuntimeException("Not a host:port pair: " + target);
146    }
147    String hostname;
148    int port = -1;
149    if (!target.contains("/")) {
150      if (colonIndex == -1) {
151        hostname = target;
152      } else {
153        // must be the old style <host>:<port>
154        hostname = target.substring(0, colonIndex);
155        port = Integer.parseInt(target.substring(colonIndex + 1));
156      }
157    } else {
158      // a new uri
159      URI addr = new Path(target).toUri();
160      hostname = addr.getHost();
161      port = addr.getPort();
162    }
163
164    if (port == -1) {
165      port = defaultPort;
166    }
167  
168    if (getStaticResolution(hostname) != null) {
169      hostname = getStaticResolution(hostname);
170    }
171    return new InetSocketAddress(hostname, port);
172  }
173
174  /**
175   * Adds a static resolution for host. This can be used for setting up
176   * hostnames with names that are fake to point to a well known host. For e.g.
177   * in some testcases we require to have daemons with different hostnames
178   * running on the same machine. In order to create connections to these
179   * daemons, one can set up mappings from those hostnames to "localhost".
180   * {@link NetUtils#getStaticResolution(String)} can be used to query for
181   * the actual hostname. 
182   * @param host
183   * @param resolvedName
184   */
185  public static void addStaticResolution(String host, String resolvedName) {
186    synchronized (hostToResolved) {
187      hostToResolved.put(host, resolvedName);
188    }
189  }
190  
191  /**
192   * Retrieves the resolved name for the passed host. The resolved name must
193   * have been set earlier using 
194   * {@link NetUtils#addStaticResolution(String, String)}
195   * @param host
196   * @return the resolution
197   */
198  public static String getStaticResolution(String host) {
199    synchronized (hostToResolved) {
200      return hostToResolved.get(host);
201    }
202  }
203  
204  /**
205   * This is used to get all the resolutions that were added using
206   * {@link NetUtils#addStaticResolution(String, String)}. The return
207   * value is a List each element of which contains an array of String 
208   * of the form String[0]=hostname, String[1]=resolved-hostname
209   * @return the list of resolutions
210   */
211  public static List <String[]> getAllStaticResolutions() {
212    synchronized (hostToResolved) {
213      Set <Entry <String, String>>entries = hostToResolved.entrySet();
214      if (entries.size() == 0) {
215        return null;
216      }
217      List <String[]> l = new ArrayList<String[]>(entries.size());
218      for (Entry<String, String> e : entries) {
219        l.add(new String[] {e.getKey(), e.getValue()});
220      }
221    return l;
222    }
223  }
224  
225  /**
226   * Returns InetSocketAddress that a client can use to 
227   * connect to the server. Server.getListenerAddress() is not correct when
228   * the server binds to "0.0.0.0". This returns "127.0.0.1:port" when
229   * the getListenerAddress() returns "0.0.0.0:port".
230   * 
231   * @param server
232   * @return socket address that a client can use to connect to the server.
233   */
234  public static InetSocketAddress getConnectAddress(Server server) {
235    InetSocketAddress addr = server.getListenerAddress();
236    if (addr.getAddress().getHostAddress().equals("0.0.0.0")) {
237      addr = new InetSocketAddress("127.0.0.1", addr.getPort());
238    }
239    return addr;
240  }
241  
242  /**
243   * Same as getInputStream(socket, socket.getSoTimeout()).<br><br>
244   * 
245   * From documentation for {@link #getInputStream(Socket, long)}:<br>
246   * Returns InputStream for the socket. If the socket has an associated
247   * SocketChannel then it returns a 
248   * {@link SocketInputStream} with the given timeout. If the socket does not
249   * have a channel, {@link Socket#getInputStream()} is returned. In the later
250   * case, the timeout argument is ignored and the timeout set with 
251   * {@link Socket#setSoTimeout(int)} applies for reads.<br><br>
252   *
253   * Any socket created using socket factories returned by {@link #NetUtils},
254   * must use this interface instead of {@link Socket#getInputStream()}.
255   *     
256   * @see #getInputStream(Socket, long)
257   * 
258   * @param socket
259   * @return InputStream for reading from the socket.
260   * @throws IOException
261   */
262  public static InputStream getInputStream(Socket socket) 
263                                           throws IOException {
264    return getInputStream(socket, socket.getSoTimeout());
265  }
266  
267  /**
268   * Returns InputStream for the socket. If the socket has an associated
269   * SocketChannel then it returns a 
270   * {@link SocketInputStream} with the given timeout. If the socket does not
271   * have a channel, {@link Socket#getInputStream()} is returned. In the later
272   * case, the timeout argument is ignored and the timeout set with 
273   * {@link Socket#setSoTimeout(int)} applies for reads.<br><br>
274   * 
275   * Any socket created using socket factories returned by {@link #NetUtils},
276   * must use this interface instead of {@link Socket#getInputStream()}.
277   *     
278   * @see Socket#getChannel()
279   * 
280   * @param socket
281   * @param timeout timeout in milliseconds. This may not always apply. zero
282   *        for waiting as long as necessary.
283   * @return InputStream for reading from the socket.
284   * @throws IOException
285   */
286  public static InputStream getInputStream(Socket socket, long timeout) 
287                                           throws IOException {
288    return (socket.getChannel() == null) ? 
289          socket.getInputStream() : new SocketInputStream(socket, timeout);
290  }
291  
292  /**
293   * Same as getOutputStream(socket, 0). Timeout of zero implies write will
294   * wait until data is available.<br><br>
295   * 
296   * From documentation for {@link #getOutputStream(Socket, long)} : <br>
297   * Returns OutputStream for the socket. If the socket has an associated
298   * SocketChannel then it returns a 
299   * {@link SocketOutputStream} with the given timeout. If the socket does not
300   * have a channel, {@link Socket#getOutputStream()} is returned. In the later
301   * case, the timeout argument is ignored and the write will wait until 
302   * data is available.<br><br>
303   * 
304   * Any socket created using socket factories returned by {@link #NetUtils},
305   * must use this interface instead of {@link Socket#getOutputStream()}.
306   * 
307   * @see #getOutputStream(Socket, long)
308   * 
309   * @param socket
310   * @return OutputStream for writing to the socket.
311   * @throws IOException
312   */  
313  public static OutputStream getOutputStream(Socket socket) 
314                                             throws IOException {
315    return getOutputStream(socket, 0);
316  }
317  
318  /**
319   * Returns OutputStream for the socket. If the socket has an associated
320   * SocketChannel then it returns a 
321   * {@link SocketOutputStream} with the given timeout. If the socket does not
322   * have a channel, {@link Socket#getOutputStream()} is returned. In the later
323   * case, the timeout argument is ignored and the write will wait until 
324   * data is available.<br><br>
325   * 
326   * Any socket created using socket factories returned by {@link #NetUtils},
327   * must use this interface instead of {@link Socket#getOutputStream()}.
328   * 
329   * @see Socket#getChannel()
330   * 
331   * @param socket
332   * @param timeout timeout in milliseconds. This may not always apply. zero
333   *        for waiting as long as necessary.
334   * @return OutputStream for writing to the socket.
335   * @throws IOException   
336   */
337  public static OutputStream getOutputStream(Socket socket, long timeout) 
338                                             throws IOException {
339    return (socket.getChannel() == null) ? 
340            socket.getOutputStream() : new SocketOutputStream(socket, timeout);            
341  }
342  
343  /**
344   * This is a drop-in replacement for 
345   * {@link Socket#connect(SocketAddress, int)}.
346   * In the case of normal sockets that don't have associated channels, this 
347   * just invokes <code>socket.connect(endpoint, timeout)</code>. If 
348   * <code>socket.getChannel()</code> returns a non-null channel,
349   * connect is implemented using Hadoop's selectors. This is done mainly
350   * to avoid Sun's connect implementation from creating thread-local 
351   * selectors, since Hadoop does not have control on when these are closed
352   * and could end up taking all the available file descriptors.
353   * 
354   * @see java.net.Socket#connect(java.net.SocketAddress, int)
355   * 
356   * @param socket
357   * @param endpoint 
358   * @param timeout - timeout in milliseconds
359   */
360  public static void connect(Socket socket, 
361                             SocketAddress endpoint, 
362                             int timeout) throws IOException {
363    if (socket == null || endpoint == null || timeout < 0) {
364      throw new IllegalArgumentException("Illegal argument for connect()");
365    }
366    
367    SocketChannel ch = socket.getChannel();
368    
369    if (ch == null) {
370      // let the default implementation handle it.
371      socket.connect(endpoint, timeout);
372    } else {
373      SocketIOWithTimeout.connect(ch, endpoint, timeout);
374    }
375
376    // There is a very rare case allowed by the TCP specification, such that
377    // if we are trying to connect to an endpoint on the local machine,
378    // and we end up choosing an ephemeral port equal to the destination port,
379    // we will actually end up getting connected to ourself (ie any data we
380    // send just comes right back). This is only possible if the target
381    // daemon is down, so we'll treat it like connection refused.
382    if (socket.getLocalPort() == socket.getPort() &&
383        socket.getLocalAddress().equals(socket.getInetAddress())) {
384      LOG.info("Detected a loopback TCP socket, disconnecting it");
385      socket.close();
386      throw new ConnectException(
387        "Localhost targeted connection resulted in a loopback. " +
388        "No daemon is listening on the target port.");
389    }
390  }
391  
392  /** 
393   * Given a string representation of a host, return its ip address
394   * in textual presentation.
395   * 
396   * @param name a string representation of a host:
397   *             either a textual representation its IP address or its host name
398   * @return its IP address in the string format
399   */
400  public static String normalizeHostName(String name) {
401    if (Character.digit(name.charAt(0), 16) != -1) { // it is an IP
402      return name;
403    } else {
404      try {
405        InetAddress ipAddress = InetAddress.getByName(name);
406        return ipAddress.getHostAddress();
407      } catch (UnknownHostException e) {
408        return name;
409      }
410    }
411  }
412  
413  /** 
414   * Given a collection of string representation of hosts, return a list of
415   * corresponding IP addresses in the textual representation.
416   * 
417   * @param names a collection of string representations of hosts
418   * @return a list of corresponding IP addresses in the string format
419   * @see #normalizeHostName(String)
420   */
421  public static List<String> normalizeHostNames(Collection<String> names) {
422    List<String> hostNames = new ArrayList<String>(names.size());
423    for (String name : names) {
424      hostNames.add(normalizeHostName(name));
425    }
426    return hostNames;
427  }
428
429  /**
430   * Attempt to obtain the host name of a name specified by ip address.  
431   * Check that the node name is an ip addr and if so, attempt to determine
432   * its host name.  If the name is not an IP addr, or the actual name cannot
433   * be determined, return null.
434   * 
435   * @return Host name or null
436   */
437  private static final Pattern ipPattern = // Pattern for matching hostname to ip:port
438    Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:?\\d*");
439  public static String getHostNameOfIP(String ip) {
440    // If name is not an ip addr, don't bother looking it up
441    if(!ipPattern.matcher(ip).matches())
442      return null;
443    
444    String hostname = "";
445    try {
446      String n = ip.substring(0, ip.indexOf(':'));
447      hostname = InetAddress.getByName(n).getHostName();
448    } catch (UnknownHostException e) {
449      return null;
450    }
451    
452    return hostname; 
453  }
454
455  /**
456   * Return hostname without throwing exception.
457   * @return hostname
458   */
459  public static String getHostname() {
460    try {return "" + InetAddress.getLocalHost();}
461    catch(UnknownHostException uhe) {return "" + uhe;}
462  }
463}