ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Zero Configuration Networking: Using the Java APIs, Part 2
Pages: 1, 2, 3, 4, 5, 6

Resolving a Service

With the dns-sd command-line tool, we use dns-sd -L to resolve a named service. DNS-SD deliberately separates browsing from resolving. When you browse, you get a list of names, not IP addresses. This is because, when using link-local addresses or DHCP, IP addresses can change from day to day. When using dynamically allocated ports and NAT gateways, TCP port numbers for a given service can change from day to day, too. A program that stores a service's IP address and port number in a preference file on disk may well find that tomorrow that address and port number no longer work. What remains stable for a given service instance is its name, and the Java DNS-SD API provides the DNSSD.resolve( ) call to translate—at time of use—from service instance name to the correct target host and port number for that service at that moment.



Resolving follows the same pattern as registering and browsing: first call DNSSD.resolve( ) and then provide a class that implements the ResolveListener interface. To function as a ResolveListener, a class must implement operationFailed( ) and serviceResolved( ). Under normal circumstances, the operationFailed( ) method will never be invoked.

Our serviceResolved( ) example just prints out the information it's given. When you register a service using the dns-sd tool, you can specify a list of "key=value" attributes, which are stored in the service's DNS TXT record. Our serviceResolved( ) method prints out those, too:


for (int i = 0; i < txtRecord.size(  ); i++)
  {
  String key = txtRecord.getKey(i);
  String value = txtRecord.getValueAsString(i);
  if (key.length(  ) > 0) System.out.println("\t" + key + "=" + value);
  }

This example, for illustrative purposes, iterates through the whole TXT record, printing out every key it finds. In a real program, you would write code to retrieve just the specific named keys that you care about, using txtRecord.contains("key") when you just want to know if a given key is present, and txtRecord.getValue("key") or txtRecord.getValueAsString("key") to retrieve the value associated with a given named key.

Example 8-3 shows a complete listing, which you can compile with javac, to resolve a named service using DNS-SD. In the case of a multi-homed host, you may receive more than one successful resolve event (e.g., if the same named service is reachable via both Ethernet and wireless). In this example, the program just runs for five seconds, displaying resolve events as they arrive, and then calls r.stop( ) and exits. Ideally, in a real program, instead of using a fixed timeout, you'd present some indication to the user that the program was attempting to connect and let the user decide how long to wait before clicking the Cancel button to give up.

Example 8-3. Java program to resolve a named DNS-SD service

import com.apple.dnssd.*;

class TestResolve implements ResolveListener
  {
  // Display error message on failure
  public void operationFailed(DNSSDService service, int errorCode)
    {
    System.out.println("Resolve failed " + errorCode);
    System.exit(-1);
    }

  // Display information when service is resolved
  public void serviceResolved(DNSSDService resolver, int flags, int ifIndex,
    String fullName, String hostName, int port, TXTRecord txtRecord)
    {
    System.out.println("Service Resolved: " + hostName + ":" + port);
    System.out.println("Flags: " + flags +
      ", ifIndex: " + ifIndex + ", FQDN: " + fullName);

    for (int i = 0; i < txtRecord.size(  ); i++)
      {
      String key = txtRecord.getKey(i);
      String value = txtRecord.getValueAsString(i);
      if (key.length(  ) > 0) System.out.println("\t" + key + "=" + value);
      }
    }

  public TestResolve(String name, String domain)
    throws DNSSDException, InterruptedException
    {
    System.out.println("TestResolve Starting");
    DNSSDService r = DNSSD.resolve(0, DNSSD.ALL_INTERFACES,
      name, "_example._tcp", domain, this);
    System.out.println("TestResolve Running");
    Thread.sleep(5000);
    System.out.println("TestResolve Stopping");
    r.stop(  );
    }

  public static void main(String[] args)
    {
    if (args.length != 2)
      {
      System.out.println("Usage: java TestResolve name domain");
      System.exit(-1);
      }
    else
      {
      try
        {
        new TestResolve(args[0], args[1]);
        }
      catch(Exception e)
        {
        e.printStackTrace(  );
        System.exit(-1);
        }
      }
    }
  }

After you've compiled TestResolve, we'll test it by registering a fake service using the dns-sd command:


% dns-sd -R "My Chosen Name" _example._tcp local 123 key=val anotherkey=anotherval

Now you can use your TestResolve program to look up that service:


% java TestResolve "My Chosen Name" local
TestResolve Starting
Service Resolved: mymac.local.:123
Flags: 0, ifIndex: 5, FQDN: My\032Chosen\032Name._example._tcp.local.
        key=val
        anotherkey=anotherval
TestResolve Running
TestResolve Stopping

As with the browsing example, it's common for the resolve to succeed so quickly that the program gets the result before it's even had time to print out its "TestResolve Running" line. The reason DNS-SD operations are asynchronous is not because they usually take a long time, but because occasionally they might, particularly when there's some kind of network problem; and it is precisely at those times—when struggling with other problems—that the user will be least forgiving toward your program if it decides to lock up and become unresponsive.

You'll see that TestResolve finds our registered service on this host, listening (we pretend) on port 123, with two TXT record attributes, key=val and anotherkey=anotherval.

Now that you know not only how to register, browse, and resolve, but also how to access named attributes in the TXT record, it's time to revisit our registration example and add a TXT record full of attributes to it.

Pages: 1, 2, 3, 4, 5, 6

Next Pagearrow