22

We just upgraded to Java 8 on Amazon Linux. We are using Spring 4.3.8.RELEASE. It used to be that we could get our machine hostname by setting up beans in our application context file like so ...

<bean id="localhostInetAddress" class="java.net.InetAddress" factory-method="getLocalHost" />
<bean id="hostname" factory-bean="localhostInetAddress" factory-method="getHostName" />

But with Java 8, the bean "hostname" now contains the string

localhost

Before Java 8, it used to contain the "hostname" value as run on the command line, which is

[myuser@machine1 ~]$ hostname
machine1.mydomain.org

How can I reconfigure our bean so that it gets the hostname that the command line lists out? I don't want to hard-code anything anywhere.

Eugene
  • 102,901
  • 10
  • 149
  • 252
Dave
  • 17,420
  • 96
  • 300
  • 582
  • Do you have a security manager running? From [the documentation of `getLocalHost()`](https://docs.oracle.com/javase/8/docs/api/java/net/InetAddress.html#getLocalHost--): “*If the operation is not allowed, an InetAddress representing the loopback address is returned.*” You may also try using `getCanonicalHostName()` to get an fqn rather than `getHostName()` – Holger Jan 18 '18 at 16:23
  • You mean security manager for Java 8? I mean if there were some kind of Linux security manager it wouldn't permit the operation you're talking about regardless of Java version, right? – Dave Jan 18 '18 at 16:40
  • I suppose, the documentation specifically referred to Java-side security managers, but of course, operating system side restrictions affect the outcome too. But it’s not clear whether the Java API can guaranty a particular behavior for that case… – Holger Jan 18 '18 at 16:44
  • I ask because it probably wouldn't be the operating system otherwise my previous calls with pre-Java 8 wouldn't have worked. Do you know where I'd like for Java-side security managers? – Dave Jan 18 '18 at 17:09
  • You can simply test whether [`System.getSecurityManager()`](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) returns something that is not `null`. – Holger Jan 18 '18 at 17:12
  • System.getSecurityManager does indeed return null. Additionally, I tried the "getCanonicalName" but the result is the same (it returns "localhost" instead of the hostname) – Dave Jan 18 '18 at 20:16
  • Then, I can’t recognize a problem from the API side. Might be an implementation detail or operating system specific thing, I don’t know. I hope, there will be other users who know more about this… – Holger Jan 19 '18 at 08:48
  • 1
    @Holger well, I see comments in our code that actually do `exec(hostname)`... and they state that on Amazon `InetAddress.getLocalHost()` returns (sometimes) the loopback device, which is `localhost`. I might need to ping the author of these lines to understand what is going on actually – Eugene Jan 19 '18 at 09:16
  • What do you have in /etc/hosts for localhost? Is ipv6 enabled? Could be related to that. – Luis Muñoz Jan 19 '18 at 23:06
  • In case you are using EC2 instances you can also try to use `ec2-metadata --public-hostname` command. Which will return the public DNS record for that host. Still that one will not exactly be the one which you are searching for. Basically, we are querying the Route53 for exact hostname, so if you are interested I can share a code snippet. – Babl Jan 23 '18 at 12:29
  • @Babi, I am indeed using EC2. If you have a Java solution that I can create a Spring bean from, yes, please share the solution. Thx – Dave Jan 23 '18 at 15:44
  • Have you consulted the EC2 documentation? https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/util/EC2MetadataUtils.html. It seams you are looking for `public-hostname` – Hannes Jan 26 '18 at 17:38
  • maybe `strace` `hostname` and a minimal java program calling `getHostName` and compare what they're doing? – the8472 Jan 26 '18 at 23:28

9 Answers9

5

From InetAddress java 8 is not getting the hostname :

There was similar bug fired in JDK.

What I understand is that they changed default resolution process.

They honor configuration in /etc/nsswitch.conf where hosts are configured for /etc/hosts that gives it main priority for name resolution.

Usually /etc/hosts has record for 127.0.0.1 localhost that provide name for host localhost

Community
  • 1
  • 1
mikep
  • 3,631
  • 6
  • 20
  • 3
    If I chage the /etc/hosts file, won't taht affect other things on my Linux system? If the answer is "yes" or "maybe," I'd prefer to use a solution that is isolated to my Java webapp/app container. – Dave Jan 21 '18 at 18:27
3

I believe Java still calls the native gethostname(), but couldn't say why this fails.

For Linux, you could try this alternative:

String hostname = new String(Files.readAllBytes(Paths.get("/proc/sys/kernel/hostname")), StandardCharsets.US_ASCII);
Marcos Zolnowski
  • 2,578
  • 1
  • 24
  • 28
3

There are a similar question in the OpenJDK bugs

The newer calls respect the localhosts /etc/nsswitch.conf configuration files. In the case of this machine that file tells these calls to look in files before referencing other naming services.

Since the /etc/hosts file contains an explicit mapping for this hostname / IP combination, that is what is returned.

In the older JDK's the gethostbyname actually ignored the local machines settings and immediately delegated to the naming service.

You can always use the Runtime class for that :

Process hostname = Runtime.getRuntime().exec("hostname");

BufferedReader stdInput = new BufferedReader(new
            InputStreamReader(hostname.getInputStream()));
String s;
while ((s = stdInput.readLine()) != null) {
        System.out.println(s);
}

But it's not so recommended as you can see

Daniel Taub
  • 3,766
  • 4
  • 30
  • 63
2

You could try InetAddress.getCanonicalHostName()

The JavaDocs for getCanonicalHostName() says

Gets the fully qualified domain name for this IP address. Best effort method, meaning we may not be able to return the FQDN depending on the underlying system configuration.

LukeN
  • 21
  • 2
  • Heya, If you're suggesting the same thing that Holger suggested in comments, I gave taht a go without success. – Dave Jan 24 '18 at 17:54
2

You really have three options of differing complexity and information value:

  1. Actually run the hostname command using Runtime. It's fairly portable and should work pretty much everywhere. (Maybe not on IBM's Mainframes)
  2. Grab all available network interfaces and get their hostnames. https://docs.oracle.com/javase/tutorial/networking/nifs/listing.html
  3. Set an environment or system property as the value of hostname.
    Like java -Dmy.hostname=hostname ...

Sad part is that it's not that straightforward in Spring XML.

All other options are known to give varying degrees of accuracy. Like 127.0.0.1 will likely resolve to localhost or localhost.localdomain.

Aleksandr Panzin
  • 1,750
  • 1
  • 13
  • 11
  • 1. https://stackoverflow.com/questions/5711084/java-runtime-getruntime-getting-output-from-executing-a-command-line-program 2. The link literally has code in it 3. https://stackoverflow.com/questions/7054972/java-system-properties-and-environment-variables – Aleksandr Panzin Jan 30 '18 at 08:26
2

Alternatively, you can run the hostname linux command using Java Runtime, code should be something like below:

String cmdResult="";
Runtime r = Runtime.getRuntime();
Process p = r.exec("hostname");
BufferedReader in = new BufferedReader(new
InputStreamReader(p.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
                cmdResult += inputLine;
      }
in.close();
Rahul
  • 537
  • 3
  • 14
2

Since my comment got lost in the stream, please try

<bean id="hostname" class="com.amazonaws.util.EC2MetadataUtils" factory-method="getLocalHostName()">

Have a look at Amazon's documemtation if the result does not suits your needs.

Hannes
  • 1,819
  • 20
  • 31
  • My develpoment envirionment is a Mac High Sierra machine. Woudl the above still work in that envirionment? – Dave Jan 27 '18 at 17:41
  • Certainly not because the info is retrieved from the EC2 rest services. But you certainly have a spring test profile where you would just set the string value. – Hannes Jan 27 '18 at 19:14
2

InetAddress in Java 8 had a bug regarding the resolution of hostname. The bug was reported in version 8u20 and has been fixed since then. Please check that you are not on this version. If not, then using InetAddress.getLocalHost().getHostName() will give you the hostname.

Also I suggest, setting up a static hostname on the AWS instance.

  1. Update /etc/hosts
  2. Update /etc/hostname
  3. Run hostname to check the hostname
  4. Reboot and check the persisted hostname

This should allow you to read the hostname from the file if required.

Also, if hostname command is giving the correct result, then use exec method in Runtime class to execute the command and the read the InputStream to get the response.

PS: You can use InetAddress.getLocalHost().isLoopbackAddress() to check whether the address is a LoopbackAddress and then use a fallback method.

Arpit Sharma
  • 878
  • 4
  • 11
1

I can be better to define a property in your .properties file for hostname like.

hostname=myserver.mydomain.com

And use it in your application. This way is not harcoded. It also avoid dependency to operating system config which can be changed without attention.

Mehmet Sunkur
  • 2,277
  • 13
  • 21
  • I don't want the domain hard-coded anywheer. It already exists in the system. WHy can't I just pull it from there? – Dave Jan 27 '18 at 17:48
  • Your host name configuration is environment variable based. Jvm8+ releases will work only dns based hostname lookups. So you need change your hostname config. Because jvm's behaviour will not change any more. Best practice is add your hostname at the end of line which is starting with 127.0.0.1 in hosts file. This will not not effect any other functions. – Mehmet Sunkur Jan 27 '18 at 19:14
  • Maybe the host name/instance name is exposed via export/env. Like `vcap` env in IBM bluemix. – Hannes Jan 27 '18 at 19:17