I've been writing about the server side implementation aspects of Bonjour for Java a while ago, and I promised to explain the client side, which is a bit more tricky. The reasons therefore is that it makes heavy use of asynchronous callbacks, leaving you no other choice but to implement some interfaces and understand the way things work behind the curtain.

So what is the client's task? In our example it is to find, that is discover, services and make them accessible to our application. Of course, it uses the same mDNSResponder facilites discussed previously. To mention it again, mDNSResponder is, from our point of view, the single instance taking care about all actions required to either register, discover or browse services. And caused by the way it works ( by "talking" to the other peers on your network ) it can't provide instant answers to requests made, that's why asynchronous callbacks are required.

We used that killerapp example, and I'm going to continue with that, though any other name can of course be used.

I assume you already imported the apple package and read about how to install it if you're unable to compile your class file.

import com.apple.dnssd.*;

Before actually starting to putting it all together, let's have a look at two interfaces that are important for our work. The one is BrowseListener, which actually defines the callbacks to call when a service is either lost or found.

public void serviceFound(DNSSDService browser,
                         int flags,
                         int ifIndex,
                         java.lang.String serviceName,
                         java.lang.String regType,
                         java.lang.String domain)

public void serviceLost(DNSSDService browser,
                        int flags,
                        int ifIndex,
                        java.lang.String serviceName,
                        java.lang.String regType,
                        java.lang.String domain)

The other interface which is used to provide our application with the data necessary to connect to a discovered service using standard Java Sockets, is ResolveListener. This interface defines only one callback, serviceResolved

public void serviceResolved(DNSSDService resolver,
                            int flags,
                            int ifIndex,
                            java.lang.String fullName,
                            java.lang.String hostName,
                            int port,
                            TXTRecord txtRecord)

which is called whenever Bonjour was apple to resolve a service. Of course, because of the BaseListener interface, operationFailed must be implemented, too. Let's do something useful now.

First of all, we have to enable Bonjour and tell it what to call in case it finds something. It's best done by having a Class which handles all the discovery stuff. So whatever your class is, just implement the interface BrowseListener, as the ResolveListener callbacks will be implemented using anonymous classes later. For me, the skeleton looks like this:

public class Discover implements BrowseListener {
	// the constructor.
	public Discover(){

	}

	public void serviceLost(DNSSDService browser, int flags, int ifIndex,
				String serviceName, String regType, String domain) {
	}

	public void serviceFound(DNSSDService browser, int flags, int ifIndex,
				String serviceName, String regType, String domain) {
	}

        public void operationFailed(DNSSDService arg0, int arg1) {
		// this one is required by BaseListener, which is the parent of all other Listener Interfaces.
	}
}

Now I'm going to initialise Bonjour's browsing capabilities in the constructor. Take care, It throws an exception if something goes wrong. It's up to you what to do with it.

	public Discover(){
		try {
			browser = DNSSD.browse("_killerapp._tcp", this);
		} catch (DNSSDException e) {
			// do something fancy here.
		}
	}

The method browse requires two arguments, the name of the service you are browsing for, and the callback handler, which is the calling object in that case. Bonjour handles the rest for us, and either one of the methods serviceFound or serviceLost gets called accordingly.

Assuming you are lucky and Bonjour finds a service with that name, serviceFound will be called. To resolve the found service to an IP address and a port, you need to call DNSSD.resolve, a method with the following signature

public static DNSSDService resolve(int flags,
                                   int ifIndex,
                                   java.lang.String serviceName,
                                   java.lang.String regType,
                                   java.lang.String domain,
                                   ResolveListener listener)
                            throws DNSSDException

It's okay to fill 0 in flags, for further meaning just read the Javadoc. Because our serviceFound method is already called with most of the required arguments, it's quite easy to implement the resolve process.

DNSSD.resolve(0, ifIndex, serviceName, regType, domain, new ResolveListener(){
				public void serviceResolved(DNSSDService resolver, int flags, int ifIndex,
				String fullname, String hostname, int port, TXTRecord txtRecord){
					InetAddress theAddress;
					try {
						theAddress = InetAddress.getByName(hostname);
					} catch (UnknownHostException e) {
						// ouch..
					}
				}

				public void operationFailed(DNSSDService arg0, int arg1) {
					// ouch, again!
				}
			});

So what's happening here is that we create an anonymous class to handle the callbacks, which should look familiar if you've been doing some AWT stuff. Once the resolve process was successful, the serviceResolved method is called, and we resolve the hostname given there to create an InetAddress object filled with all the information we want to know.

And yes, that's it. The same applies for the serviceLost method, which has the same signature, and it's up to you what to do with that address object from now on! Hope this helped you a bit.