24

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.

13
  • Do you have a security manager running? From the documentation of 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() Commented Jan 18, 2018 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? Commented Jan 18, 2018 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… Commented Jan 18, 2018 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? Commented Jan 18, 2018 at 17:09
  • 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 Commented Jan 19, 2018 at 9:16

9 Answers 9

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

Sign up to request clarification or add additional context in comments.

1 Comment

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.
4
+50

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

Comments

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); 

Comments

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.

1 Comment

Heya, If you're suggesting the same thing that Holger suggested in comments, I gave taht a go without success.
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.

1 Comment

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(); 

Comments

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.

2 Comments

My develpoment envirionment is a Mac High Sierra machine. Woudl the above still work in that envirionment?
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.
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.

Comments

0

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.

3 Comments

I don't want the domain hard-coded anywheer. It already exists in the system. WHy can't I just pull it from there?
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.
Maybe the host name/instance name is exposed via export/env. Like vcap env in IBM bluemix.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.