351

Which of the following is the best and most portable way to get the hostname of the current computer in Java?

Runtime.getRuntime().exec("hostname")

vs

InetAddress.getLocalHost().getHostName()

Malt
  • 28,965
  • 9
  • 65
  • 105
Mahendra
  • 3,647
  • 2
  • 16
  • 16
  • What technology stack is this? – tom redfern Sep 08 '11 at 13:22
  • I think the only real uname (uts_name) backed name is from the RMI/JMX VMID, but this is implementation specific. – eckes Oct 27 '15 at 18:14
  • Maybe of interest: On GNU/Linux (and certainly others), it turns out that the command `hostname(1)` results in the system call `uname(3)` (defined in POSIX), found in `/usr/include/sys/utsname.h`. (Try it by running `strace hostname`). The corresponding command is `uname(1)`. Thus to obtain the so-called "hostname", really the "nodename" one can use the command `uname --nodename` too. This should (generally) be the string that can be found in `/etc/hostname`. – David Tonhofer Jul 19 '22 at 16:00

12 Answers12

420

Strictly speaking - you have no choice but calling either hostname(1) or - on Unix gethostname(2). This is the name of your computer. Any attempt to determine the hostname by an IP address like this

InetAddress.getLocalHost().getHostName()

is bound to fail in some circumstances:

  • The IP address might not resolve into any name. Bad DNS setup, bad system setup or bad provider setup may be the reason for this.
  • A name in DNS can have many aliases called CNAMEs. These can only be resolved in one direction properly: name to address. The reverse direction is ambiguous. Which one is the "official" name?
  • A host can have many different IP addresses - and each address can have many different names. Two common cases are: One ethernet port has several "logical" IP addresses or the computer has several ethernet ports. It is configurable whether they share an IP or have different IPs. This is called "multihomed".
  • One Name in DNS can resolve to several IP Addresses. And not all of those addresses must be located on the same computer! (Usecase: A simple form of load-balancing)
  • Let's not even start talking about dynamic IP addresses.

Also don't confuse the name of an IP-address with the name of the host (hostname). A metaphor might make it clearer:

There is a large city (server) called "London". Inside the city walls much business happens. The city has several gates (IP addresses). Each gate has a name ("North Gate", "River Gate", "Southampton Gate"...) but the name of the gate is not the name of the city. Also you cannot deduce the name of the city by using the name of a gate - "North Gate" would catch half of the bigger cities and not just one city. However - a stranger (IP packet) walks along the river and asks a local: "I have a strange address: 'Rivergate, second left, third house'. Can you help me?" The local says: "Of course, you are on the right road, simply go ahead and you will arrive at your destination within half an hour."

This illustrates it pretty much I think.

The good news is: The real hostname is usually not necessary. In most cases any name which resolves into an IP address on this host will do. (The stranger might enter the city by Northgate, but helpful locals translate the "2nd left" part.)

In the remaining corner cases you must use the definitive source of this configuration setting - which is the C function gethostname(2). That function is also called by the program hostname.

Rop
  • 3,359
  • 3
  • 38
  • 59
A.H.
  • 63,967
  • 15
  • 92
  • 126
  • 15
    Not quite the answer I was hoping for but now I know how to ask a better question, thanks. – Sam Hasler Oct 19 '11 at 14:21
  • 3
    See also http://stackoverflow.com/questions/6050011/how-do-i-get-the-local-hostname-if-unresolvable-through-dns-in-java – Raedwald Aug 22 '13 at 15:09
  • 8
    That's a nice write-up of the limitations that any software (not just a Java program) has in determining the host's name in the world. However, note that getHostName is implemented in terms of the underlying OS, presumably the same way that hostname/gethostname is. On a "normal" system, InetAddress.getLocalHost().getHostName() is equivalent to calling hostname/gethostname, so you can't really say that one fails in ways the other does not. – Peter Cardona May 10 '14 at 11:54
  • System.err.println(Runtime.getRuntime().exec("hostname")); gives me this: java.lang.UNIXProcess@6eb2384f – user152468 Apr 23 '15 at 12:31
  • I do not consider InetAddress.getLocalHost().getHostName() to be a very reliable/deterministic way for retrieving the hostname as it behaves differently on different OSes and even Linux Distributions. I'd rather use InetAddress.getByName(ip).getHostName() specifying the local address to use but thus retrieving a reliable hostname – juwi Sep 10 '15 at 11:43
  • 19
    The implementation of InetAddress.getLocalHost().getHostName() is actually very deterministic :) If you follow the source code, it ultimately calls gethostname() on Windows, and getaddrinfo() on Unixy systems. The result is the same as using your OS hostname command. Now hostname may provide an answer you don't want to use, that is possible for many reasons. Generally, software should get the hostname from the user in a config file, that way, it is always the correct hostname. You could use InetAddress.getLocalhost().getHostName() as a default if the user does not provide a value. – Greg Sep 12 '15 at 19:13
  • 1
    Also note that the InetAddress.getLocalHost javadoc states: `Returns the address of the local host. This is achieved by retrieving the name of the host from the system, then resolving that name into an InetAddress.` Which suggests that the hostname is found first and the IP address is obtained from that (not the other way around). Also note that Runtime.exec() involves a security manager check, so may be blocked based on security policy. – Mr Weasel Feb 22 '22 at 20:25
111

InetAddress.getLocalHost().getHostName() is the more portable way.

exec("hostname") actually calls out to the operating system to execute the hostname command.

Here are a couple other related answers on SO:

EDIT: You should take a look at A.H.'s answer or Arnout Engelen's answer for details on why this might not work as expected, depending on your situation. As an answer for this person who specifically requested portable, I still think getHostName() is fine, but they bring up some good points that should be considered.

Community
  • 1
  • 1
Nick Knowlson
  • 7,185
  • 6
  • 47
  • 63
  • 13
    Running getHostName() results in an error some times. E.g., here is what I get in an Amazon EC2 AMI Linux instance: java.net.UnknownHostException: Name or service not known java.net.InetAddress.getLocalHost(InetAddress.java:1438) – Marquez Mar 18 '13 at 13:42
  • 3
    Also, `InetAddress.getLocalHost()` in some cases returns the loopback device. In that case, `getHostName()` returns "localhost", which is not very useful. – olenz Jul 21 '16 at 11:50
78

As others have noted, getting the hostname based on DNS resolution is unreliable.

Since this question is unfortunately still relevant in 2018, I'd like to share with you my network-independent solution, with some test runs on different systems.

The following code tries to do the following:

  • On Windows

    1. Read the COMPUTERNAME environment variable through System.getenv().

    2. Execute hostname.exe and read the response

  • On Linux

    1. Read the HOSTNAME environment variable through System.getenv()

    2. Execute hostname and read the response

    3. Read /etc/hostname (to do this I'm executing cat since the snippet already contains code to execute and read. Simply reading the file would be better, though).

The code:

public static void main(String[] args) throws IOException {
    String os = System.getProperty("os.name").toLowerCase();

    if (os.contains("win")) {
        System.out.println("Windows computer name through env:\"" + System.getenv("COMPUTERNAME") + "\"");
        System.out.println("Windows computer name through exec:\"" + execReadToString("hostname") + "\"");
    } else if (os.contains("nix") || os.contains("nux") || os.contains("mac os x")) {
        System.out.println("Unix-like computer name through env:\"" + System.getenv("HOSTNAME") + "\"");
        System.out.println("Unix-like computer name through exec:\"" + execReadToString("hostname") + "\"");
        System.out.println("Unix-like computer name through /etc/hostname:\"" + execReadToString("cat /etc/hostname") + "\"");
    }
}

public static String execReadToString(String execCommand) throws IOException {
    try (Scanner s = new Scanner(Runtime.getRuntime().exec(execCommand).getInputStream()).useDelimiter("\\A")) {
        return s.hasNext() ? s.next() : "";
    }
}

Results for different operating systems:

macOS 10.13.2

Unix-like computer name through env:"null"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:""

OpenSuse 13.1

Unix-like computer name through env:"machinename"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:""

Ubuntu 14.04 LTS This one is kinda strange since echo $HOSTNAME returns the correct hostname, but System.getenv("HOSTNAME") does not:

Unix-like computer name through env:"null"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:"machinename
"

EDIT: According to legolas108, System.getenv("HOSTNAME") works on Ubuntu 14.04 if you run export HOSTNAME before executing the Java code.

Windows 7

Windows computer name through env:"MACHINENAME"
Windows computer name through exec:"machinename
"

Windows 10

Windows computer name through env:"MACHINENAME"
Windows computer name through exec:"machinename
"

The machine names have been replaced but I kept the capitalization and structure. Note the extra newline when executing hostname, you might have to take it into account in some cases.

Charlie
  • 8,530
  • 2
  • 55
  • 53
Malt
  • 28,965
  • 9
  • 65
  • 105
  • 7
    If you `export HOSTNAME` before running the Java code on Ubuntu 14.04 LTS it is also returned by `System.getenv("HOSTNAME")`. – legolas108 Jan 13 '16 at 15:10
  • You have to explicitly `export HOSTNAME` for Java to use it? I thought it should be a default env var like PATH. – David May 16 '16 at 19:46
  • 6
    Yes, this is one of the Java bugs that will never be fixed for religious reasons. A related one is to be able to set the process name. Bugs logged almost 20 years ago. – Tuntable Jun 12 '17 at 05:10
  • Excellent answer, very thorough! I don't know why you use that strange delimiter, especially considering every printed output has a strange newline in it. Regardless, I'm updating the answer to work with MacOS, shorten the indexOf to contains calls, and fixing the OS variable name to be more consistent with standard variable name conventions. – Charlie Feb 06 '18 at 08:29
  • 1
    @Charlie the `\A` delimiter is just a hack for reading the entire InputStream using a `Scanner`. – Malt Feb 06 '18 at 09:21
  • Sure it shouldn't be "\A" instead of "\\A"? – Charlie Feb 06 '18 at 17:04
  • @Charlie try it with "\A". You'll get a compilation error since `\A` is not an escape sequence (unlike `\n` or `\r` or similar sequences), so we need to escape the escape... – Malt Feb 06 '18 at 17:15
  • Why does the code expect that sequence? I'm picturing a backslash and capital A being printed to the console... Why look for that? Is there some undocumented behavior in the scanner? – Charlie Feb 06 '18 at 22:19
  • Oh, stupid me, I didn't realize it was looking for a regex. Thanks! – Charlie Feb 07 '18 at 02:57
  • Anyway, if we're using regex to ~parse~ the result of the exec, might as well strip the newline off the end; no one looking for a hostname is going to want that newline in their String. – Charlie Feb 07 '18 at 03:02
28

InetAddress.getLocalHost().getHostName() is better (as explained by Nick), but still not very good

One host can be known under many different hostnames. Usually you'll be looking for the hostname your host has in a specific context.

For example, in a web application, you might be looking for the hostname used by whoever issued the request you're currently handling. How to best find that one depends on which framework you're using for your web application.

In some kind of other internet-facing service, you'll want the hostname your service is available through from the 'outside'. Due to proxies, firewalls etc this might not even be a hostname on the machine your service is installed on - you might try to come up with a reasonable default, but you should definitely make this configurable for whoever installs this.

Arnout Engelen
  • 6,709
  • 1
  • 25
  • 36
24

Although this topic has already been answered there's more to say.

First of all: Clearly we need some definitions here. The InetAddress.getLocalHost().getHostName() gives you the name of the host as seen from a network perspective. The problems with this approach are well documented in the other answers: it often requires a DNS lookup, it's ambiguous if the host has multiple network interfaces and it just plain fails sometimes (see below).

But on any OS there's another name as well. A name of the host that gets defined very early in the boot process, long before the network is initialized. Windows refers to this as computername, Linux calls it kernel hostname and Solaris uses the word nodename. I like best the word computername, so I'll use that word from now on.

Finding the computername

  • On Linux/Unix the computername is what you get from the C function gethostname(), or hostname command from shell or HOSTNAME environment variable in Bash-like shells.

  • On Windows the computername is what you get from environment variable COMPUTERNAME or Win32 GetComputerName function.

Java has no way of obtaining what I've defined as 'computername'. Sure, there are workarounds as described in other answers, like for Windows calling System.getenv("COMPUTERNAME"), but on Unix/Linux there's no good workaround without resorting to JNI/JNA or Runtime.exec(). If you don't mind a JNI/JNA solution then there's gethostname4j which is dead simple and very easy to use.

Let's move on with two examples, one from Linux and one from Solaris, which demonstrate how you can easily get into a situation where you cannot obtain the computername using standard Java methods.

Linux example

On a newly created system, where the host during installation has been named as 'chicago', we now change the so-called kernel hostname:

$ hostnamectl --static set-hostname dallas

Now the kernel hostname is 'dallas', as evident from the hostname command:

$ hostname
dallas

But we still have

$ cat /etc/hosts
127.0.0.1   localhost
127.0.0.1   chicago

There's no misconfiguration in this. It just means the host's networked name (or rather the name of the loopback interface) is different from the host's computername.

Now, try executing InetAddress.getLocalHost().getHostName() and it will throw java.net.UnknownHostException. You are basically stuck. There's no way to retrieve neither the value 'dallas' nor the value 'chicago'.

Solaris example

The example below is based on Solaris 11.3.

The host has deliberately been configured so that the loopback name <> nodename.

In other words we have:

$ svccfg -s system/identity:node listprop config
...
...
config/loopback             astring        chicago
config/nodename             astring        dallas

and the contents of /etc/hosts :

:1 chicago localhost
127.0.0.1 chicago localhost loghost

and the result of the hostname command would be:

$ hostname
dallas

Just like in the Linux example a call to InetAddress.getLocalHost().getHostName() will fail with

java.net.UnknownHostException: dallas:  dallas: node name or service name not known

Just like the Linux example you are now stuck. There's no way to retrieve neither the value 'dallas' nor the value 'chicago'.

When will you really struggle with this?

Very often you'll find that InetAddress.getLocalHost().getHostName() will indeed return a value which is equal to the computername. So there's no problem (except for the added overhead of name resolution).

The problem arises typically within PaaS environments where there's a difference between computername and the name of the loopback interface. For example people report problems in Amazon EC2.

Bug/RFE reports

A bit of searching reveals this RFE report : link1, link2. However, judging from the comments on that report the issue seems to have been largely misunderstood by the JDK team, so it is unlikely it will be addressed.

I like the comparison in the RFE to other programming languages.

peterh
  • 18,404
  • 12
  • 87
  • 115
18

Just one-liner ... cross platform (Windows-Linux-Unix-Mac(Unix)) [Always works, No DNS required]:

String hostname = new BufferedReader(
    new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
   .readLine();

You're done !!

Dan Ortega
  • 1,679
  • 17
  • 13
  • InetAddress.getLocalHost().getHostName() will not work in Unix ? – Satish Patro May 30 '20 at 09:40
  • 2
    That works but requires dns resolution, what if you are connected to a wifi tethering from your phone (to mention just one example) it wont have a DNS knowing the localhost machine and it won't work. – Dan Ortega May 31 '20 at 02:39
  • A bit differently: `Process p = Runtime.getRuntime().exec("hostname"); byte[] bytes = p.getInputStream().readAllBytes(); String hostname = new String(bytes,"ASCII").trim();` Note th `.trim()` to get rid of the spurious EOL generated by the `hostname` (or alternatively `uname --nodename`) command. The whole sequence needs to be enclosed in a try/catch. – David Tonhofer Jul 19 '22 at 16:49
14

Environment variables may also provide a useful means -- COMPUTERNAME on Windows, HOSTNAME on most modern Unix/Linux shells.

See: https://stackoverflow.com/a/17956000/768795

I'm using these as "supplementary" methods to InetAddress.getLocalHost().getHostName(), since as several people point out, that function doesn't work in all environments.

Runtime.getRuntime().exec("hostname") is another possible supplement. At this stage, I haven't used it.

import java.net.InetAddress;
import java.net.UnknownHostException;

// try InetAddress.LocalHost first;
//      NOTE -- InetAddress.getLocalHost().getHostName() will not work in certain environments.
try {
    String result = InetAddress.getLocalHost().getHostName();
    if (StringUtils.isNotEmpty( result))
        return result;
} catch (UnknownHostException e) {
    // failed;  try alternate means.
}

// try environment properties.
//      
String host = System.getenv("COMPUTERNAME");
if (host != null)
    return host;
host = System.getenv("HOSTNAME");
if (host != null)
    return host;

// undetermined.
return null;
Community
  • 1
  • 1
Thomas W
  • 13,940
  • 4
  • 58
  • 76
  • 7
    StringUtils? What is that?? (I know what you mean I just think it is bad karma to bring in an external library for this sole purpose .. and on top of that not even mention it) – peterh Apr 13 '14 at 14:50
  • 29
    It's bad karma to constantly write "empty" checks manually. Very many projects have such checks done manually (the way you suggest) and inconsistently, with many resulting bugs. Use a library. – Thomas W Apr 13 '14 at 23:38
  • 20
    In case anyone sees this and is actually confused by `StringUtils`, its provided by the Apache commons-lang project. Using it, or something like it, is highly recommended. – JBCP May 02 '14 at 20:21
  • Somehow `System.getenv("HOSTNAME")` came out null on Mac via Beanshell for Java, but `PATH` was extracted ok. I could also do `echo $HOSTNAME` on Mac too, just System.getenv("HOSTNAME") seem to have issue. Strange. – David May 16 '16 at 19:38
7

If you're not against using an external dependency from maven central, I wrote gethostname4j to solve this problem for myself. It just uses JNA to call libc's gethostname function (or gets the ComputerName on Windows) and returns it to you as a string.

https://github.com/mattsheppard/gethostname4j

Matt Sheppard
  • 116,545
  • 46
  • 111
  • 131
  • Thanks Matt, but this doesn't seem to work anymore. It blocks indefinitely on my macOS 11.4. – Saket Jun 28 '21 at 04:59
  • Ah - thanks for the heads up - I'll try to check it out. I think the only machine I have with 11.4 is an Apple Silicon one, so that might prove interesting :) – Matt Sheppard Jun 29 '21 at 10:33
  • I was unable to reproduce the issue, but would welcome a discussion (maybe in a github issue over there rather than here) with anyone who can reproduce it. – Matt Sheppard Sep 29 '21 at 06:33
6

The most portable way to get the hostname of the current computer in Java is as follows:

import java.net.InetAddress;
import java.net.UnknownHostException;

public class getHostName {

    public static void main(String[] args) throws UnknownHostException {
        InetAddress iAddress = InetAddress.getLocalHost();
        String hostName = iAddress.getHostName();
        //To get  the Canonical host name
        String canonicalHostName = iAddress.getCanonicalHostName();

        System.out.println("HostName:" + hostName);
        System.out.println("Canonical Host Name:" + canonicalHostName);
    }
}
stolsvik
  • 5,253
  • 7
  • 43
  • 52
Desta Haileselassie Hagos
  • 23,140
  • 7
  • 48
  • 53
  • 8
    Portability aside, how does this method compare to the method in the question? What are pros/cons? – Marquez Mar 18 '13 at 13:40
5
hostName == null;
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
{
    while (interfaces.hasMoreElements()) {
        NetworkInterface nic = interfaces.nextElement();
        Enumeration<InetAddress> addresses = nic.getInetAddresses();
        while (hostName == null && addresses.hasMoreElements()) {
            InetAddress address = addresses.nextElement();
            if (!address.isLoopbackAddress()) {
                hostName = address.getHostName();
            }
        }
    }
}
Sam Hasler
  • 12,344
  • 10
  • 72
  • 106
0

Building off of Dan Ortega's answer, I created a generic executeCommand(String) method that takes a command as a paramater.

import java.io.*;

public class SystemUtil {
  public static void main(String[] args) throws IOException {
    System.out.println(retrieveHostName());
  }
     
  public static String retrieveHostName() throws IOException {
    return executeCommand("hostname");
  }
     
  private static String executeCommand(String command) throws IOException {
    return new BufferedReader(
        new InputStreamReader(Runtime.getRuntime().exec(command).getInputStream()))
      .readLine();
  }
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
-3

InetAddress.getLocalHost().getHostName() is the best way out of the two as this is the best abstraction at the developer level.

java_mouse
  • 2,069
  • 4
  • 21
  • 30