I'd like to get an id unique to a computer with Java, on Windows, MacOS and, if possible, Linux. It could be a disk UUID, motherboard S/N...
Runtime.getRuntime().exec can be used (it is not an applet).
Ideas?
The problem with MAC address is that there can be many network adapters connected to the computer. Most of the newest ones have two by default (wi-fi + cable). In such situation one would have to know which adapter's MAC address should be used. I tested MAC solution on my system, but I have 4 adapters (cable, WiFi, TAP adapter for Virtual Box and one for Bluetooth) and I was not able to decide which MAC I should take... If one would decide to use adapter which is currently in use (has addresses assigned) then new problem appears since someone can take his/her laptop and switch from cable adapter to wi-fi. With such condition MAC stored when laptop was connected through cable will now be invalid.
For example those are adapters I found in my system:
lo MS TCP Loopback interface
eth0 Intel(R) Centrino(R) Advanced-N 6205
eth1 Intel(R) 82579LM Gigabit Network Connection
eth2 VirtualBox Host-Only Ethernet Adapter
eth3 Sterownik serwera dostepu do sieci LAN Bluetooth
Code I've used to list them:
Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
while (nis.hasMoreElements()) {
NetworkInterface ni = nis.nextElement();
System.out.println(ni.getName() + " " + ni.getDisplayName());
}
From the options listen on this page, the most acceptable for me, and the one I've used in my solution is the one by #Ozhan Duz, the other one, similar to #finnw answer where he used JACOB, and worth mentioning is com4j - sample which makes use of WMI is available here:
ISWbemLocator wbemLocator = ClassFactory.createSWbemLocator();
ISWbemServices wbemServices = wbemLocator.connectServer("localhost","Root\\CIMv2","","","","",0,null);
ISWbemObjectSet result = wbemServices.execQuery("Select * from Win32_SystemEnclosure","WQL",16,null);
for(Com4jObject obj : result) {
ISWbemObject wo = obj.queryInterface(ISWbemObject.class);
System.out.println(wo.getObjectText_(0));
}
This will print some computer information together with computer Serial Number. Please note that all classes required by this example has to be generated by maven-com4j-plugin. Example configuration for maven-com4j-plugin:
<plugin>
<groupId>org.jvnet.com4j</groupId>
<artifactId>maven-com4j-plugin</artifactId>
<version>1.0</version>
<configuration>
<libId>565783C6-CB41-11D1-8B02-00600806D9B6</libId>
<package>win.wmi</package>
<outputDirectory>${project.build.directory}/generated-sources/com4j</outputDirectory>
</configuration>
<executions>
<execution>
<id>generate-wmi-bridge</id>
<goals>
<goal>gen</goal>
</goals>
</execution>
</executions>
</plugin>
Above's configuration will tell plugin to generate classes in target/generated-sources/com4j directory in the project folder.
For those who would like to see ready-to-use solution, I'm including links to the three classes I wrote to get machine SN on Windows, Linux and Mac OS:
Java code to get computer SN on Windows
Java code to get computer SN on Linux
Java code to get computer SN on Mac OS
The OSHI project provides platform-independent hardware utilities.
Maven dependency:
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>LATEST</version>
</dependency>
For instance, you could use something like the following code to identify a machine uniquely:
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.ComputerSystem;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem;
class ComputerIdentifier
{
static String generateLicenseKey()
{
SystemInfo systemInfo = new SystemInfo();
OperatingSystem operatingSystem = systemInfo.getOperatingSystem();
HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware();
CentralProcessor centralProcessor = hardwareAbstractionLayer.getProcessor();
ComputerSystem computerSystem = hardwareAbstractionLayer.getComputerSystem();
String vendor = operatingSystem.getManufacturer();
String processorSerialNumber = computerSystem.getSerialNumber();
String processorIdentifier = centralProcessor.getIdentifier();
int processors = centralProcessor.getLogicalProcessorCount();
String delimiter = "#";
return vendor +
delimiter +
processorSerialNumber +
delimiter +
processorIdentifier +
delimiter +
processors;
}
public static void main(String[] arguments)
{
String identifier = generateLicenseKey();
System.out.println(identifier);
}
}
Output for my machine:
Microsoft#57YRD12#Intel64 Family 6 Model 60 Stepping 3#8
Your output will be different since at least the processor serial number will differ.
It is common to use the MAC address is associated with the network card.
The address is available in Java 6 through through the following API:
Java 6 Docs for Hardware Address
I haven't used it in Java, but for other network identification applications it has been helpful.
What do you want to do with this unique ID? Maybe you can do what you want without this ID.
The MAC address maybe is one option but this is not an trusted unique ID because the user can change the MAC address of a computer.
To get the motherboard or processor ID check on this link.
On Windows only, you can get the motherboard ID using WMI, through a COM bridge such as JACOB.
Example:
import java.util.Enumeration;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.EnumVariant;
import com.jacob.com.Variant;
public class Test {
public static void main(String[] args) {
ComThread.InitMTA();
try {
ActiveXComponent wmi = new ActiveXComponent("winmgmts:\\\\.");
Variant instances = wmi.invoke("InstancesOf", "Win32_BaseBoard");
Enumeration<Variant> en = new EnumVariant(instances.getDispatch());
while (en.hasMoreElements())
{
ActiveXComponent bb = new ActiveXComponent(en.nextElement().getDispatch());
System.out.println(bb.getPropertyAsString("SerialNumber"));
break;
}
} finally {
ComThread.Release();
}
}
}
And if you choose to use the MAC address to identify the machine, you can use WMI to determine whether an interface is connected via USB (if you want to exclude USB adapters.)
It's also possible to get a hard drive ID via WMI but this is unreliable.
Not Knowing all of your requirements. For example, are you trying to uniquely identify a computer from all of the computers in the world, or are you just trying to uniquely identify a computer from a set of users of your application. Also, can you create files on the system?
If you are able to create a file. You could create a file and use the creation time of the file as your unique id. If you create it in user space then it would uniquely identify a user of your application on a particular machine. If you created it somewhere global then it could uniquely identify the machine.
Again, as most things, How fast is fast enough.. or in this case, how unique is unique enough.
Be careful when using the MAC address as an identifier. I've experienced several gotchas:
On OS X, ethernet ports that are not active/up do not show up in the NetworkInterface.getNetworkInterfaces() Enumeration.
It's insanely easy to change a MAC address on cards if you've got appropriate OS privileges.
Java has a habit of not correctly identifying "virtual" interfaces. Even using the NetworkInterface.isVirtual() won't always tell you the truth.
Even with the above issues, I still think it's the best pure Java approach to hardware locking a license.
I think you should look at this link ... you can make a mixed key using several
identifiers such as mac+os+hostname+cpu id+motherboard serial number.
The usage of MAC id is most easier way if the task is about logging the unique id a system.
the change of mac id is though possible, even the change of other ids of a system are also possible is that respective device is replaced.
so, unless what for a unique id is required is not known, we may not be able to find an appropriate solution.
However, the below link is helpful extracting mac addresses.
http://www.stratos.me/2008/07/find-mac-address-using-java/
For identifying a windows machine uniquely.
Make sure when you use wmic to have a strategy of alternative methods. Since "wmic bios get serialnumber" might not work on all machines, you might need to have additional methods:
# Get serial number from bios
wmic bios get serialnumber
# If previous fails, get UUID
wmic csproduct get UUID
# If previous fails, get diskdrive serialnumber
wmic DISKDRIVE get SerialNumber
Resources:
The Best Way To Uniquely Identify A Windows Machine
http://www.nextofwindows.com/the-best-way-to-uniquely-identify-a-windows-machine/
In the java programs I have written for release I used the motherboard serial number (which is what I beleive windows use); however, this only works on windows as my function creates a temporary VB script which uses the WMI to retrieve the value.
public static String getMotherboardSerial() {
String result = "";
try {
File file = File.createTempFile("GetMBSerial",".vbs");
file.deleteOnExit();
FileWriter fw = new FileWriter(file);
String vbs =
"Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n"
+ "Set colItems = objWMIService.ExecQuery _ \n"
+ " (\"Select * from Win32_ComputerSystemProduct\") \n"
+ "For Each objItem in colItems \n"
+ " Wscript.Echo objItem.IdentifyingNumber \n"
+ "Next \n";
fw.write(vbs);
fw.close();
Process gWMI = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath());
BufferedReader input = new BufferedReader(new InputStreamReader(gWMI.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
result += line;
System.out.println(line);
}
input.close();
}
catch(Exception e){
e.printStackTrace();
}
result = result.trim();
return result;
}
Related
Is it just my setup or is anyone else having this problem?
Using AdoptOpenJDK 1.8.0_275 installed at:
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre
API docs of System.getProperties() do not specify any details.
Can confirm this is still happening on adoptopenjdk14, as well as openjdk early access build for j16.
You can file a bug if you want, but I bet it'll be denied. At this point, the name Mac OS X is not so much 'the name of the OS' as a 'globally agreed upon keyword identifying that unix-based mac operating system', where I mean globally literally (as in, 'around the planet', not 'across your source base/VM'). Changing it would just break stuff needlessly. The same applies, to a lesser degree, to version 10.16: The thing before the dot is not so much 'this is how the OS identifies itself' and more a 'globally agreed upon versioning scheme for Mac OS, identifying a wide and ill defined set of capabilities'.
There is no meaningful difference between the transition between big sur and catalina, other than the fact that apple made a marketing decision. If you want to point at an OS transition that might warrant the entirely nebulous choice to consider it a 'major change', surely it was the one to catalina, as that made by far the largest changes (including removing support for 32-bit entirely) in the last bunch of releases.
This leaves you with the challenge of: Okay, great, I can use System.getProperty("os.name") to get the globally agreed upon keyword that means unix-like Mac OS, and os.version for a string I can break into bits to figure out some nebulous batch of capabilities, but what if I need the actual name of the OS to e.g. show to a user?
Then you have three major options:
The easy one is to just write mapping code. Acknowledge that os.name and os.version give you (rather arguably) useful intent and not so much official names, and therefore, write some mappings. These would map name/version pairs to rendering strings, falling back to just printing the name string and the version string, concatenated, verbatim. You could add a mapping: Mac OS X/10.16 → Mac OS Big Sur in this table.
The hard way: Figure out you're on a mac (which is now easier; os.name returns Mac OS X, or just check for the existence: Files.isExecutable(Paths.get("/usr/bin/sw_vers"))), and then use ProcessBuilder to execute /usr/bin/sw_vers, picking up all output into a big string, and then parse it. Its output looks like:
ProductName: macOS
ProductVersion: 11.1
BuildVersion: 20C69
which, crucially, doesn't even include the words Big Sur, but does give you 11.1. I don't know how to run a command line tool that actually gives you Big Sur. Maybe system_profiler, but note that this takes many minutes to run, I really doubt you want to run that.
NB: you can also run .command("/usr/bin/sw_vers", "-productVersion") which gives you just 11.1, this may be a lot simpler to parse. -productName also works, gives you just macOS.
If you need this information to scan for OS capabilities, then stop doing this. It doesn't work with browsers, and it's not a good plan for OS releases either. What capability are you scanning for? Imagine, for example, if it is 'Can I run /usr/bin/sw_vers to figure stuff out', as a hypothetical example. The right strategy is NOT to check os.name/os.version, conclude that the command must exist, and then run it, failing catastrophically if it is not there. The right move is to check if /usr/bin/sw_vers exists, and then execute it, falling back to some non-mac based solution (perhaps /usr/bin/uname) in other cases. Scan for the capability, don't scan for the OS/version.
Java code to call native tool sw_vers
Regarding Option # 2 in the Answer by rzwitserloot, here is a complete code example to run from Java a command-line tool sw_vers that describes the version of macOS software running on the host computer.
If on the command-line (console) such as in Terminal.app, you run:
sw_vers
…in Big Sur on an Intel Mac we get:
ProductName: macOS
ProductVersion: 11.2
BuildVersion: 20D64
We only need the middle piece. So running:
sw_vers -productVersion
…shows simply 11.2, the value we need for your purpose.
Here is complete example app with a method to return this string into Java.
ProcessBuilder class creates operating system processes. Each new process is represented by the Process class.
We use try-with-resources syntax to automatically close the InputStream and Scanner objects.
Once you have the 11.2 string in hand, split on the FULL STOP, pull the first number 11, and you know you are running on Big Sur.
package org.example;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* Example code showing how to get current version of macOS from Java
* by running a native command-line tool `sw_vers`.
*/
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
String version = this.getMacOsVersionNumber();
System.out.println( "version = " + version );
}
public String getMacOsVersionNumber ( )
{
String result = "";
List < String > command = List.of( "sw_vers" , " -productVersion" );
try (
InputStream inputStream = new ProcessBuilder( command ).start().getInputStream() ;
Scanner s = new Scanner( inputStream ).useDelimiter( "\\A" ) ;
)
{
result = s.hasNext() ? s.next() : "";
}
catch ( IOException e )
{
e.printStackTrace();
}
return Objects.requireNonNull( result );
}
}
The Raspberry Pi 3 includes BLE support. I confirmed it works by
sudo hcitool lescan
which returned the MAC and BLE 'complete local name' for neighboring advertisers.
How does one access this programmatically, in Java?
To use BLE on Raspberry Pi 3 you have to update bluez (the bluetooth core in Raspbian linux) then use the bluez D-Bus interface to interact with it.
I'm looking for writing my own java lib, but it's very difficult because there are few documentation about D-Bus in java and about bluez.
For bluez there are only the sample code in the last distribution.
For now i have write a simple script that update the bluez version to the latest:
https://gist.github.com/tongo/94338cebf4e6367d353439bca8d0a376
I have also found a blog post that talk about d-bus,java and bluez:
http://smartspacestuff.blogspot.it/2016/02/i-got-figurin-out-dbus-bluez.html
It was useful, but not very clear for me.
I hope this can help.
If you found other documentation post it.
I don't think there is a clear or easy answer available at this time. Bluetooth integration requires native components that are not part of a standard JDK.
The most common library used for using Bluetooth with Java on Linux is BlueCove. BlueCove provides extra native libraries for working with Bluetooth on linux: BlueCove-GPL or BlueCove-bluez(experimental). However, you will likely need to compile one of these yourself for use on your RPi. Methods for doing do will be dependent on your distribution and will require some significant knowledge of linux, compiling native code, etc. A quick google search shows some working examples of this for previous RPi versions. It's unclear if it will work with the BLE on RPi 3 though.
Another might be to try using "Camel Bluetooth Component", which is wrapper over Bluecove and expects libbluetooth-dev and blueman to be installed. But again, not clear if it will work with RPi 3.
If unable to get a true integration working, another option might be to simply make external Process calls out from Java to the command line Bluetooth utilities that you know already work. It depends on your use-case if this an option, but I suspect could be sufficient for many BLE specific use cases.
You can make this very simple by simply executing a command through Runtime and reading the output with a BufferedReader.
Executing the command:
Process p;
p = Runtime.getRuntime().exec(command);
p.waitFor();
Full code:
package your.package.rpicommand;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ExecuteShellCommand {
public static void main(String[] args) {
ExecuteShellCommand obj = new ExecuteShellCommand();
String domainName = "google.com";
//in mac oxs
String command = "ping -c 3 " + domainName;
//in windows
//String command = "ping -n 3 " + domainName;
String output = obj.executeCommand(command);
System.out.println(output);
}
private String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}
I wish I could of coded this for you but there is already plenty of example on the internet about this.
Although this is one way of doing this, you should use BlueCove or some library to scan through the Bluetooth devices.
Source: https://www.mkyong.com/java/how-to-execute-shell-command-from-java/
I am trying to access a device through a COM object in a JAVA interface.
The particular call (as described by the manufacturer) is:
Name: ScanUSB
Parameters: [out] VARIANT* serialNumbers
Use: serialNumbers is a pointer to a VARIANT containing an array of BSTR.
The exact call doesn't matter, but I need to feed it a BSTR array through the Java interface. A VB demo for the COM interface simply does this with the commandlm_Co1.ScanUSB(snNum), with Dim snNum As Object = Nothing. The Items in snNum are then displayed in a dropdown menu.
I am trying to do this with JACOB, as I have had the most luck with communication. This is more or less what I am using:
import com.jacob.com.Variant;
import com.jacob.com.Dispatch;
public class JunoReader {
public JunoReader() {
Dispatch oOphirLink = new Dispatch("clsid:{--the id of the device here--}");
Variant snNum = new Variant();
Variant testing = Dispatch.call(oOphirLink,"ScanUSB", snNum);
println(testing);
}
}
When I run this, everything compiles properly and I can confirm that I am communicating with the device, but all I get back is the null Variant that I put in.
My question is: How can I feed a BSTR array to a COM object through a JAVA interface?
Thanks!
So, one month and zero free time later, I have an answer to my own question and that answer is "Use Python".
Python allows users to access COM objects with the comtypes module, effectively with a single command:
from comtypes.client import CreateObject
target_guid = CreateObject("{TARGET-COM-CLSID-HERE}")
This allows python to talk with whatever the COM object is, so there was none of the trouble that Java was giving me. The single line: target_guid.ScanUSB() produced (u'717610',), the serial number of the target device. Notice that Python has no trouble reading the Unicode produced by the COM object.
The second trick involves Python's COM server, generated with the win32com module. Because the COM servers can be registered to Windows, I don't have to worry about where the dll is located. All I need is the clsid. To build the python COM server, I followed the instructions for a "quick start to server side COM and Python". So what does it all look like?
Python code:
class OphirPyCOM:
_reg_clsid_ = "{PYTHON-COM-CLSID-HERE}"
_reg_desc_ = "Python COM server"
_reg_progid_ = "Python COM server"
_public_methods_ = [ 'Hello',
'ConnectOphir',
'ScanUSB', """More methods here"""
]
_public_attrs_ = ["""Some public attributes"""]
_readonly_attrs_ = ["""some read-only attributes"""]
def __init__(self):
"""some variables declared here"""
def Hello(self, who):
"""Verifies a connection"""
return "{PYTHON-COM-CLSID-HERE}" + str(who)
def ConnectOphir(self,clsid):
"""Connects to the target COM Object"""
from comtypes.client import CreateObject
self.target_guid = CreateObject(clsid)
return "OphirLMMeasurement object created."
def ScanUSB(self):
"""Communicates with the target device"""
self.connected_inst = self.target_guid.ScanUSB()
for i in range(0,len(self.connected_inst)):
self.inst_list.append(str(self.connected_inst[i]))
return self.inst_list
if __name__ == "__main__":
# use 'python com.py' to register the COM server
# use 'python com.py --unregister' to unregister it
print "Registering COM server..."
import win32com.server.register
win32com.server.register.UseCommandLine(OphirPyCOM)
We can run that with the command line and get it registered. Then we take the values over to Java with JACOB:
import com.jacob.com.Variant;
import com.jacob.com.Dispatch;
public class JunoReader {
String pyClsid = "{PYTHON-COM-CLSID-HERE}"; // This is where your python COM clsid goes
String opClsid = "{TARGET-COM-CLSID-HERE}"; // This is where your ultimate target clsid goes
public JunoReader() {
_pyClsid = "clsid:" + pyClsid
// This finds the COM object location:
Dispatch oOphirLink = new Dispatch(_pyClsid);
}
public String HandShake() {
String _talkBack = "is connected.";
Variant _handShake = Dispatch.call(oOphirLink,"Hello",_talkBack); // I am trying to look for the Juno...
return (_handShake.toString());
}
public String ConnectOphir() {
Variant _connectOphir = Dispatch.call(oOphirLink,"ConnectOphir", opClsid); // Connect to the target COM object
return (_connectOphir.toString());
}
public String ScanUSB() {
Variant _serialNumberList = Dispatch.call(oOphirLink,"ScanUSB"); // This scans the USB ports for devices
return (_serialNumberList.toString());
}
}
calling JunoReader.ScanUSB() produces: 717610, exactly like it is supposed to. Implementing the subsequent methods of the manufacturer dll allowed me to read data from this device into a Java applet. Problem solved.
A caveat: you may need to unregister the Python COM every time you change the change the source file and then re-register with a different clsid. Java was having a tough time establishing a connection to the updated code unless I used a new clsid every time I changed the file.
Why did I spend all this time typing this up? Because most of the advice related to reading native data types into Java involved some version of JNI and JNA. I spent weeks trying to get the tutorials to compile and didn't make any progress. On the other hand, I thought of this approach yesterday and can now communicate with my device through Java. The Python COM server provided a simple, straightforward way to interface Java with native applications. No UnsatisfiedLinkErrors, no Can't find librarys, no Classpath and JAVA_HOME issues. I didn't need to learn C or C++ and all the Python tutorials worked as described with no necessary modification.
To summarize, if you are having trouble reading native data types into Java, just set up a Python COM server and let Python's dynamic typing do the work for you.
I am actually trying to run OrientDB on IBMi (iSeries, AS/400, V540, JDK6-32-bit last CUM PTFs applied). One of the things that OrientDB does early on (so to speak) is try to find at least one mac address (not entirely sure why as I am using it purely in local embedded mode for now). I have made my own test method which has the same results:
LogIt.log(Level.INFO,"About to list mac addresses.");
final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
final byte[] mac = networkInterface.getHardwareAddress();
if (mac == null) {
LogIt.log(Level.INFO,"Mac address is null.");
continue;
} // if
String hex = Utils.convertBytesToHexString(mac);
LogIt.log(Level.INFO,"Mac address found {0}.",hex);
} // while
The system output shows a message:
getkerninfo 1: A system call received a parameter that is not valid.
and it fails to retrieve any mac addresses using the call getHardwareAddress() (actually it returns null 3 times). I have run it as a 'programmer' type user and as a security officer (like 'root'), with the same results. It works fine on my Ubuntu 12.04 PC and laptop.
I would like to know if anyone else has come across this problem and found a solution.
Since getkerninfo() is a generally undocumented C system call on Unix/Linux, are you running this in PASE on your iSeries? I would first suspect that that's the only likely environment that might give success. Otherwise, a Linux partition seems called for.
This sounds like something that should have been asked before, and it has sort of, but I'm looking to get the local hostname and IP addresses of a machine even when it is not resolvable through DNS (in Java).
I can get the local IP addresses without resolution by iterating through NetworkInterfaces.getNetworkInterfaces().
Any answers to this question I've found indicate to use getLocalHost()
InetAddress localhost = java.net.InetAddress.getLocalHost();
hostName = localhost.getHostName();
but this throws an UnknownHostException if the hostname isn't resolvable through DNS.
Is there no way to get the local hostname without a DNS lookup happening behind the scenes?
edit: the IP address retrieved is 10.4.168.23
The exception is java.net.UnknownHostException: cms1.companyname.com: cms1.companyname.com (hostname changed for pseudo-anonymity), and the hosts file does not contain the hostname. But it does know its hostname, so I'm not sure why I can't get it without an exception being thrown.
Yes, there should be a way in Java to get the hostname without resorting to name service lookups but unfortunately there isn't.
However, you can do something like this:
if (System.getProperty("os.name").startsWith("Windows")) {
// Windows will always set the 'COMPUTERNAME' variable
return System.getenv("COMPUTERNAME");
} else {
// If it is not Windows then it is most likely a Unix-like operating system
// such as Solaris, AIX, HP-UX, Linux or MacOS.
// Most modern shells (such as Bash or derivatives) sets the
// HOSTNAME variable so lets try that first.
String hostname = System.getenv("HOSTNAME");
if (hostname != null) {
return hostname;
} else {
// If the above returns null *and* the OS is Unix-like
// then you can try an exec() and read the output from the
// 'hostname' command which exist on all types of Unix/Linux.
// If you are an OS other than Unix/Linux then you would have
// to do something else. For example on OpenVMS you would find
// it like this from the shell: F$GETSYI("NODENAME")
// which you would probably also have to find from within Java
// via an exec() call.
// If you are on zOS then who knows ??
// etc, etc
}
}
and that will get you 100% what you want on the traditional Sun JDK platforms (Windows, Solaris, Linux) but becomes less easy if your OS is more excotic (from a Java perspective). See the comments in the code example.
I wish there was a better way.
This question is old, but unfortunately still relevant since it's still not trivial to get a machine's host name in Java. Here's my solution with some test runs on different systems:
public static void main(String[] args) throws IOException {
String OS = System.getProperty("os.name").toLowerCase();
if (OS.indexOf("win") >= 0) {
System.out.println("Windows computer name throguh env:\"" + System.getenv("COMPUTERNAME") + "\"");
System.out.println("Windows computer name through exec:\"" + execReadToString("hostname") + "\"");
} else {
if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0) {
System.out.println("Linux computer name throguh env:\"" + System.getenv("HOSTNAME") + "\"");
System.out.println("Linux computer name through exec:\"" + execReadToString("hostname") + "\"");
System.out.println("Linux computer name through /etc/hostname:\"" + execReadToString("cat /etc/hostname") + "\"");
}
}
}
public static String execReadToString(String execCommand) throws IOException {
Process proc = Runtime.getRuntime().exec(execCommand);
try (InputStream stream = proc.getInputStream()) {
try (Scanner s = new Scanner(stream).useDelimiter("\\A")) {
return s.hasNext() ? s.next() : "";
}
}
}
Results for different operating systems:
OpenSuse 13.1
Linux computer name throguh env:"machinename"
Linux computer name through exec:"machinename
"
Linux 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 (this however might be an issue with my environment only):
Linux computer name throguh env:"null"
Linux computer name through exec:"machinename
"
Linux computer name through /etc/hostname:"machinename
"
Windows 7
Windows computer name throguh env:"MACHINENAME"
Windows computer name through exec:"machinename
"
The machine names have been replaced for (some) anonymization, but I've kept the capitalization and structure. Note the extra newline when executing hostname, you might have to take it into account in some cases.
Alternatively, use JNA to call the gethostname function on unixes, avoiding the reverse DNS lookup.
Some notes: on Linux, I believe gethostname simply calls uname and parses the output. On OS X the situation is more complex, as your hostname can be affected by DNS, but those side-effects aside, it's definitely what I get from hostname.
import com.sun.jna.LastErrorException
import com.sun.jna.Library
import com.sun.jna.Native
...
private static final C c = (C) Native.loadLibrary("c", C.class);
private static interface C extends Library {
public int gethostname(byte[] name, int size_t) throws LastErrorException;
}
public String getHostName() {
byte[] hostname = new byte[256];
c.gethostname(hostname, hostname.length)
return Native.toString(hostname)
}
jna-platform.jar includes Win32 functions, so there it's as simple as a call to Kernel32Util.getComputerName().
If you are getting 127.0.0.1 as the IP address then you may need to locate your Operating System specific hosts file and add a mapping to your hostname in it.
This is a bit of a hack. But you could launch a new Process from Java and run the hostname command. Reading the outputstream of the child process would give you the name of the localhost.
Java will read the /etc/hosts file if there is no DNS configured, or rather the corresponding C functions will.
on Linux read
/proc/sys/kernel/hostname
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
(I think it's almost exactly what #danny-thomas proposed, but maybe more convenient if you're already using Maven ;)