views:

313

answers:

9

Dear all,

I have been struggling to find the "description" of the computer on which my Java application is running.

What I'm after is the name used for DNS when advertising my computer on the local network ("iMac Mattijs" in the screen shots below).

On Windows XP, this name can be found here: Control Panel -> System -> Computer Name -> Computer Description.

alt text

On Mac OS 10.6, this name can be found here: System Preferences -> Sharing -> Computer Name

alt text

The methods below don't deliver the name I'm looking for. Have a look at this code:

    System.out.println("COMPUTERNAME environment variable: " + System.getenv("COMPUTERNAME"));
    try { System.out.println("localhost name: " + InetAddress.getLocalHost().getHostName()); } 
    catch (UnknownHostException e1) {}

    try {
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface thisInterface = interfaces.nextElement();
            Enumeration<InetAddress> addresses = thisInterface.getInetAddresses();

            System.out.println("* network interface: " + thisInterface.getDisplayName());
             while (addresses.hasMoreElements()) {
                 InetAddress address = addresses.nextElement();
                 System.out.println(" - address: " + address.getCanonicalHostName());
             }
        }           
    } catch (SocketException e) {}

On Windows, this prints:

COMPUTERNAME environment variable: ARTTECH-51CA5F5
localhost name: arttech-51ca5f5
* network interface: MS TCP Loopback interface
 - address: localhost
* network interface: NVIDIA nForce Networking Controller - Packet Scheduler Miniport
* network interface: Broadcom 802.11n Network Adapter - Packet Scheduler Miniport
 - address: arttech-51ca5f5.lan
* network interface: Bluetooth Device (Personal Area Network)

On Mac, I get:

COMPUTERNAME environment variable: null
localhost name: imac-mattijs.lan 
* network interface: en1
 - address: imac-mattijs.lan
 - address: imac-mattijs.local
* network interface: lo0
 - address: localhost
 - address: fe80:0:0:0:0:0:0:1%1
 - address: localhost

But I am looking for the full String "iMac Mattijs".

Any clues would be very welcome!

Thanks, Mattijs

+2  A: 

Are you sure you're not just after the COMPUTERNAME environment variable, which is what you see in that control panel window?

Michael Goldshteyn
Hi Michael, thanks for your reply. The COMPUTERNAME variable returns the Full computer name, a somewhat cryptical string including some seemingly random numbers and characters. What I need is the more readable computer description (windows) / computer name (mac os), as explained above.
Mattijs
@Mathijs: that name is not usually in DNS.
reinierpost
+4  A: 

You should know that a single computer can have multiple DNS names.

Try this to obtain a name:

  • Call java.net.NetworkInterface.getNetworkInterfaces() to get all the network interfaces;
  • For each NetworkInterface returned, call java.net.NetworkInterface.getInetAddresses() to get all the IP addresses of an interface;
  • For each IP address returned, call java.net.InetAddress.getCanonicalHostName() to get the associated hostname;
  • Choose one of the hostnames.
Steve Emmerson
Hi Steve, thanks for your reply. I tried your suggestion but unfortunately this route doesn't deliver the computer name I need as described in my question but the Full computer name.
Mattijs
@Mattijs Odd. The documentation on `InetAddress.getCanonicalHostName()` indicates that it returns the fully-qualified hostname if at all possible. What does it return?
Steve Emmerson
Hi Steve, I added screen shots to my original post to illustrate the issue. `InetAddress.getCanonicalHostName()` returns arttech-51ca5f5, but I am looking for the name "iMac Mattijs"
Mattijs
@Mattijs Is only one name returned by the method I described? If not, is the name you're looking for amongst the other names?
Steve Emmerson
@Steve: I added your suggestion to my original post, together with the results. Unfortunately none of the names returned represent the literal computer description, including capital letters etc..
Mattijs
@Mattijs In that case, it's likely that the computer name you're looking for is only available to the operating-system. and to Mac-specific programs.
Steve Emmerson
+2  A: 

How about using InetAddress.getHostName()?

System.out.println(
 "Name: " + java.net.InetAddress.getLocalHost().getHostName() );

Edit: So the above answer is obviously not what you wanted. However... The MAST project does provide a method to retrieve the computer description. See SysUtils.getComputerDescription(). This method is OS specific. The project homepage says that an OSX release is in the works.

Andy
@Andy: indeed, looking at the API, it seems MAST found a way to do what I need on Windows. Unfortunately their source doesn't seem to be open.
Mattijs
Yeah, I noticed that the project is closed. Quite unfortunate. I imagine that they just wrote a JNI library to do all the dirty work, so you could semi-easily do the same if you need it enough.
Andy
A: 

That Computer Description/Name happens to be the hostname of the localhost. you can verify this by checking your terminal prompt, which typically shows ComputerName:CurrentDirectory User$ corresponding to a default bash prompt export PS1="\u@\h\w: "

So using:

try { 
     InetAddress addr = InetAddress.getLocalHost();

     // Get hostname 
     String hostname = addr.getHostName();
} catch (UnknownHostException e) { } 

you should be able to get the localhost name as you want. I hope this helps.

ref:

`

posdef
How is this different from the answer that I posted?
Andy
besides the little paragraph there is no particular difference... I might have missed it, my bad..
posdef
@posdef: as you can see in my question, unfortunately this method does not return the name I need.
Mattijs
@Mathijs: i see.. It seems i have missed not only @Andy's reply but also the details of your question... I should really pay more attention to reading.. :)
posdef
A: 

Allright, I found a way to do this myself, but only on Mac OS. The bounty is still open for people that can come up with a way to answer my question on Windows.

Running this app returns Computer Description: iMac Mattijs

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;

import com.apple.dnssd.*;

/**
 * Example of finding your computer description. Tested on Mac OS 10.6, Java 1.6
 * 
 * Note: make sure you're not compiling with JavaSE-1.6 but with JVM 1.6.0 (MacOS X Default), otherwise access to DNSSD libraries will be restricted.
 * 
 * @author mattijskneppers
 */

public class DescriptionFinder implements BrowseListener, ResolveListener {
    InetAddress local = null;
    HashMap<DNSSDService, String> fileSharingNameMap = new HashMap<DNSSDService, String>();

    public static void main(String[] args) {
        new DescriptionFinder();
    }   

    public DescriptionFinder() {
        try { local = InetAddress.getLocalHost(); } catch (UnknownHostException e) { System.out.println("Error, couldn't resolve local host"); }

        try {
            DNSSD.browse("_afpovertcp._tcp", this);
        } catch (DNSSDException e) {
            System.out.println("DeviceFinder: problem browsing for new file sharing");
            e.printStackTrace();
        }       
    }

    @Override
    public void operationFailed(DNSSDService arg0, int arg1) {}

    @Override
    public void serviceResolved(DNSSDService resolver, int flags, int ifIndex, String fullName, String hostName, int port, TXTRecord txtRecord) {
        InetAddress[] addresses = null;
        try {
            addresses = InetAddress.getAllByName(hostName);
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        InetAddress ip = null;
        for (InetAddress address : addresses) {
            if (!address.isLinkLocalAddress()) {
                ip = address;
                break;
            }
        }

        String fileSharingName = fileSharingNameMap.get(resolver);
        if (fileSharingName != null) {
            if (ip.equals(local)) {
                System.out.println("Computer Description: " + fileSharingName);
            }
        }       
    }

    @Override
    public void serviceFound(DNSSDService service, int flags, int ifIndex, String serviceName, String regType, String domain) {
        //System.out.println("found file sharing: " + serviceName + ", domain: " + domain + ", regType: " + regType + ", flags: " + flags);
        try {
            DNSSDService resolver = DNSSD.resolve(flags, ifIndex, serviceName, regType, domain, this);
            fileSharingNameMap.put(resolver, serviceName);
        } catch (DNSSDException e) {
            System.out.println("DeviceFinder: problem resolving new filesharing device");
            e.printStackTrace();
        }           
    }

    @Override
    public void serviceLost(DNSSDService service, int flags, int ifIndex, String serviceName, String regType, String domain) {}
}
Mattijs
This will only work if the user has File Sharing turned on.
Jeremy W. Sherman
@Jeremy: you are right. Your solution is absolutely the way to go.
Mattijs
+3  A: 

I don't think its possible to get this without going native. If you can find a java library that gives you access to WMI then you can get it from the object Win32_OperatingSystem in the field Description.

Googling gives a few likely options;

http://henryranch.net/software/jwmi-query-windows-wmi-from-java/
Very simple (and free) if a little hacky. Seems to work by writing .vbs scripts to a temp dir and invoking them with Runtime.getRuntime().exec() and cscript.exe.

https://com4j.dev.java.net/

http://sourceforge.net/projects/jacob-project/
Java COM bridge.

I think the Microsoft JVM has native bits included in it that may help too.

Qwerky
@Qwerky thanks for these pointers! I'll have a look and see if I can find a way through :)
Mattijs
+3  A: 

Mac OS X stores the computer name in the System Configuration dynamic store. The standard interface to this is via the System Configuration framework. The commandline tool exercising this API is scutil:

$ scutil --get computerName
Hermes is awesome!

(I temporarily changed my computer name to something with spaces and punctuation so it would be readily distinguishable from the hostname, which in this case would be something like hermes-is-awesome.local.)

You can interface with this pretty easily using JNI:

class SCDynamicStore {
  public native String copyComputerName();
  static {
    System.loadLibrary("SCDynamicStore");
  }
}

class HostnameSC {
  public static void
  main(String[] args) {
    SCDynamicStore store = new SCDynamicStore();
    String computerName = store.copyComputerName();
    System.out.format("computer name: %s\n", computerName);
  }
}

Now javac FILE.java and then javah SCDynamicStore. This produces SCDynamicStore.h. Copy this to SCDynamicStore.c and edit it to read:

#include "SCDynamicStore.h"
#include <SystemConfiguration/SystemConfiguration.h>

JNIEXPORT jstring JNICALL
Java_SCDynamicStore_copyComputerName(JNIEnv *env, jobject o)
{
  SCDynamicStoreRef store = NULL;
  CFStringRef computerName = NULL;
  CFStringEncoding UTF8 = kCFStringEncodingUTF8;
  CFIndex length;
  Boolean ok;
  jstring computerNameString = NULL;
  CFStringRef process = CFSTR("com.me.jni.SCDynamicStore");

  store = SCDynamicStoreCreate(NULL, process, NULL/*callout*/, NULL/*ctx*/);
  if (!store) {
    fprintf(stderr, "failed to get store\n");
    goto CantCreateStore;
  }

  computerName = SCDynamicStoreCopyComputerName(store, NULL);
  if (!computerName) {
    fprintf(stderr, "failed to copy computer name\n");
    goto CantCopyName;
  }

  length = CFStringGetLength(computerName);
  length = CFStringGetMaximumSizeForEncoding(length, UTF8);
  {
    char utf8[length];
    if (!CFStringGetCString(computerName, utf8, sizeof(utf8), UTF8)) {
      fprintf(stderr, "failed to convert to utf8\n");
      goto CantConvert;
    }
    computerNameString = (*env)->NewStringUTF(env, utf8);
  }

CantConvert:
  CFRelease(computerName);
CantCopyName:
  CFRelease(store), store = NULL;
CantCreateStore:
  return computerNameString;
}

(You could simplify the code by using Obj-C toll-free bridging and taking advantage of -[NSString UTF8String]. It might be desirable to throw an exception instead of just returning NULL in some cases of error.)

You can then compile this using clang -shared -I/Developer/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/JavaVM.framework/Headers/ -framework CoreFoundation -framework SystemConfiguration SCDynamicStore.c -o libSCDynamicStore.dylib.

Now, provided libSCDynamicStore.dylib is along LD_LIBRARY_PATH, which it will be when it's in the current directory, you can run the application:

$ java HostnameSC
computer name: Hermes is awesome!
Jeremy W. Sherman
@Jeremy: this is a great answer, it is very comprehensive, and it works!! Many thanks! For the sake of completeness, maybe you could change `java FILE.java` to `javac FILE.java` (assuming that is what you meant). Once more, thanks for your help!
Mattijs
+3  A: 

Hi Mattijs,

Here is what i found upon some experiments. The Computer Description is stored in the registry key

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\LanmanServer\Parameters\srvcomment

So, If we have any API which we can use to get the reg key values, we can find it out.

Also, Found the following link which gives a good class to query reg key values:

http://www.rgagnon.com/javadetails/java-0630.html

And, Using the class WinRegistry given in the above site, I could successfully find out the Computer Description using the code:

String computerName = WinRegistry.readString(WinRegistry.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\services\\LanmanServer\\Parameters", "srvcomment");
Balaram
@Balaram: This is a great answer, it works flawlessly! Although I marked Jeremy's answer for Mac as 'the' answer, this one fixes my problem for Windows, and you're a new user, so I awarded you the bounty! Thanks!
Mattijs
Thanks Mattijs!!!
Balaram
+3  A: 
  1. Get com4j (direct download link) which is a Java library to make calls to COM.

  2. Unzip and in the sample folder you will find a WMI sample.

  3. Change the Main.java to the following and you should be set.

    
    package wmi;

    import com4j.Com4jObject; 
    import wmi.events.ISWbemSinkEvents; 

    public class Main {
        public static void main(String[] args) throws Exception {
            System.out.println("Connecting to WMI repository");
            ISWbemLocator wbemLocator = ClassFactory.createSWbemLocator();
            ISWbemServices wbemServices = 
                    wbemLocator.connectServer(
                       "localhost","Root\\CIMv2","","","","",
                       0,null);

            System.out.println("connected");
            {
                System.out.println("Query Computer Description");
                ISWbemObjectSet result = wbemServices.execQuery(
                        "Select Description from Win32_OperatingSystem",
                          "WQL",48,null);
                for( Com4jObject obj : result ) {
                    ISWbemObject wo = obj.queryInterface(ISWbemObject.class);
                    System.out.println(wo.getObjectText_(0));
                }
            }
        }
    }

renick
@renick: Although this seems slightly more elaborate than querying the registry key directly, this is probably the official way to do this on Windows. And it's a great example! I wish I could award the bounty twice, or at least mark multiple answers as the accepted answer. I tested this and it works! Thanks!!
Mattijs