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

Example 8-6 shows a complete listing that you can compile with javac, which resolves the named service and then begins monitoring its TXT record for changes. The change compared to Example 8-1 is indicated by the comment, "New code to update TXT record begins here."

Example 8-6. Java program to monitor a TXT record for changes

import com.apple.dnssd.*;

class TestResolveWithMonitoring implements ResolveListener, QueryListener
  {
  private DNSSDService monitorQ = null;

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

  public void queryAnswered(DNSSDService query, int flags, int ifIndex,
    String fullName, int rrtype, int rrclass, byte[] rdata, int ttl)
    {
    if ((flags & 2) != 0)
      {
      boolean blankPrinted = false;
      TXTRecord txtRecord = new TXTRecord(rdata);
      for (int i = 0; i < txtRecord.size(  ); i++)
        {
        String key = txtRecord.getKey(i);
        String value = txtRecord.getValueAsString(i);
        if (key.length(  ) > 0)
          {
          if (!blankPrinted)
            {
            blankPrinted = true;
            System.out.println(  );
            }
          System.out.println("\t" + key + "=" + value);
          }
        }
      }
    }

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

    // Now that we've got a resolve result,
    // start monitoring the TXT record and stop the resolve call.
    try { monitorQ = DNSSD.queryRecord(0, ifIndex, fullName, 16, 1, this); }
    catch (Exception e) { e.printStackTrace(  ); System.exit(-1); }
    resolver.stop(  );
    Thread.sleep(1);
    }

  public TestResolveWithMonitoring(String name, String domain)
    throws DNSSDException, InterruptedException
    {
    System.out.println("TestResolveWithMonitoring Starting");
    DNSSDService r = DNSSD.resolve(0, DNSSD.ALL_INTERFACES,
      name, "_example._tcp", domain, this);
    System.out.println("TestResolveWithMonitoring Running");
    Thread.sleep(30000);
    System.out.println("TestResolveWithMonitoring Stopping");
    if (monitorQ == null) r.stop(  );
    else monitorQ.stop(  );
    try { Thread.sleep(1); }
    catch (Exception e) { e.printStackTrace(  ); System.exit(-1); }
    }

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

After you've compiled TestRegisterWithUpdates and TestResolveWithMonitoring, we can test them. In one terminal window, run:


% java TestRegisterWithUpdates "My Chosen Name"

While that's still running, in another terminal window, run TestResolveWithMonitoring:


% java TestResolveWithMonitoring "My Chosen Name" local
TestResolveWithMonitoring Starting
Service Resolved: mymac.local.:54444
Flags: 0, ifIndex: 5, FQDN: My\032Chosen\032Name._example._tcp.local.
TestResolveWithMonitoring Running

        txtvers=1
        status=Ready

        txtvers=1
        status=Steady

        txtvers=1
        status=Go
TestResolveWithMonitoring Stopping

First, the TestResolveWithMonitoring client resolves the name. After it's discovered the target host and port, it starts a query for the TXT record and stops the resolve. Now, each time the TXT record is updated, the queryAnswered method gets called with the new data.

TIP: In the first version of the Java DNS-SD API there was a bug that if you stopped one DNS-SD operation and then immediately started another, the new operation could begin reusing the same underlying file descriptor before the background thread had finished cleaning up. To avoid running into this bug there are a couple of precautions you can take:

  • Use a Thread.sleep(1); after stopping any operation, to allow the background thread to run and do its necessary cleanup.

  • If you have a sequence of code that starts and stops DNS-SD operations, particularly in a listener callback method, write your code to start all the new operations first, before it begins stopping old operations. That way the kernel won't be tempted to reuse the same file descriptors, because at the point that you start each new operation, the old operations haven't been stopped yet, so the file descriptors are still in use and aren't eligible to be recycled.

Now that we've built some toy one-page programs to demonstrate the concepts, it's time to write a real program that actually does something.

Daniel H. Steinberg is the editor for the new series of Mac Developer titles for the Pragmatic Programmers. He writes feature articles for Apple's ADC web site and is a regular contributor to Mac Devcenter. He has presented at Apple's Worldwide Developer Conference, MacWorld, MacHack and other Mac developer conferences.

Stuart Cheshire is currently a Senior Scientist with Apple Computer, specializing in Internet Protocols.


View catalog information for Zero Configuration Networking: The Definitive Guide

Return to ONJava.com.