PageRenderTime 26ms CodeModel.GetById 1ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/core/org/apache/hadoop/net/NetUtils.java

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