Thursday, August 16, 2012

Oracle Kills getLocalhost on MacOS X in Java 7

This is going to be another one of those How things that can't happen actually happen post.
I recently updated my Mac to Java 7. Pretty late though. Right after that DistributeMe services start behaving at least strange. Right after the start a service registers itself in a registry with a unique service identifier, which looks like that:

rmi://a_b_c_FooService.qhxgttedwr@192.168.1.113:9252@20120816162707


Where 192.168.1.113 being the ip adress of the machine the service runs on. This adress is used by the client, once the later wants to connect to the service.

After upgrade to Java 7 the service registered itself in the registry with the identifier:

rmi://a_b_c_FooService.qhxgttedwr@unknown:9252@20120816162707

Of course no client were able to resolve the host named unknown. I started investigating where this comes from, and well, it was in one of those sections...


 
private static String getHostName(){
  try{
    InetAddress localhost = InetAddress.getLocalHost();
    String host = localhost.getHostAddress();
    HashMap mappings = configuration.getMappings(); 
    String mappedHost = mappings.get(host);
    return mappedHost == null ? host : mappedHost;
  }catch(UnknownHostException e){
    return "unknown";
  }
}

Ok, this is certainly not the best idea to return "unknown" here (one of those, can't happen anyway bug), but why the heck did InetAddress.getLocalHost() fail?

I wrote a very small program to verify it:

package localhostbug;

import java.net.InetAddress;

public class PrintLocalhost {
  public static void main(String[] args) throws Exception{
    InetAddress localhost = InetAddress.getLocalHost();
    String host = localhost.getHostAddress();
    System.out.println("host: "+host);
  }
}

Running it with JAVA6 and JAVA7 shows the difference, watch yourself:

$JAVA6_HOME/bin/java -cp classes localhostbug.PrintLocalhost
host: 192.168.140.200
and
$JAVA7_HOME/bin/java -cp classes localhostbug.PrintLocalhost
Exception in thread "main" java.net.UnknownHostException: colin.speedport.ip: colin.speedport.ip: nodename nor servname provided, or not known
 at java.net.InetAddress.getLocalHost(InetAddress.java:1438)
 at localhostbug.PrintLocalhost.main(PrintLocalhost.java:7)
Caused by: java.net.UnknownHostException: colin.speedport.ip: nodename nor servname provided, or not known
 at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method)
 at java.net.InetAddress$1.lookupAllHostAddr(InetAddress.java:866)
 at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1258)
 at java.net.InetAddress.getLocalHost(InetAddress.java:1434)
 ... 1 more

After some googling I found a bug in oracle bug database and a similar issue in openjdk. Seems Java and Mac is getting less and less a love story.
Sad.

4 comments:

  1. As a workaround, add the following parameter
    -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.KQueueSelectorProvider

    ReplyDelete
  2. adding my local host name to /etc/hosts worked, whereas the VM option did not:

    127.0.0.1 localhost deep-thought

    ReplyDelete
  3. Leon, InetAddress.getLocalHost() is designed to give you the host's own address. But, really, what is a host's own address? This is - and has always been - a question that often have an ambiguous answer if you are on a multi-homed host. True that many people use the method and it works well for them in their little sandbox environment. But then you move your application to some bigger host that has 4 NICs and thus can be identified (accessed) by 4 different IP addresses ... then what?

    So I would argue that relying on InetAddress.getLocalHost() in the first place is simply wrong.

    ReplyDelete
    Replies
    1. From the practical point of view it used to return the first address in /etc/hosts. For a multi-homed host you usually can pick one address as bind address, but using the first as default usually worked. Now it doesn't ;-)

      Delete