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

Registering a Service with DNS TXT Record Attributes

To register a service with DNS TXT record attributes, we first need to create the TXT record:




TXTRecord txtRecord = new TXTRecord(  );
txtRecord.set("txtvers", "1");
txtRecord.set("status", "ready");
txtRecord.set("difficulty", "medium");

By convention, the first key in a TXT record should be a txtvers key, indicating the version that a client needs to have implemented in order to usefully understand the following keys in this TXT record. After the initial txtvers key, the rest of the keys are up to your protocol-creating imagination.

In this example, all the values we set are textual strings, but (despite the name) DNS TXT records are perfectly capable of holding raw binary data, too. If you have some binary data you wish to attach as an attribute, you can do so directly using the alternate form of the TXTRecord.set( ) method, which takes a raw byte array as the value:


TXTRecord.set(java.lang.String key, byte[] value)

You can store any binary data you wish, so you shouldn't feel compelled to use something like hexadecimal characters or Base-64 encoding to turn binary data into text before you store it as a key/value attribute. The only constraint is that key/value attributes are intended for storing small amounts of additional information about a service. The length of the key name, plus the length of the value data, cannot add up to more than 254 bytes—yet another reason not to double the size of your binary data by needlessly turning it into hexadecimal text.

As your program evolves over time, you may define new key names with new meanings. If you're careful, you can generally write code to be forward- and backward-compatible. If a client tries to fetch a given named key from a TXT record and finds it missing, it can conclude that it is talking to an older server that predates the invention of that key, and most of the time, it's possible to write a client to take the right steps to work with that older server. If a client communicates with a newer server that defines new key names that were invented after the client was written, the client will generally ignore those new keys—the client only calls getValue( ) for key names it knows about, and the rest simply go unnoticed. However, if you find in the future that you have no choice but to make a change to your TXT record keys that is so drastic that compatibility is simply not going to be possible, you should specify that these new TXT records have a new version number in their txtvers key. This way, as long as you had the foresight to write your first clients so they check the txtvers key and display an error message if it does not contain a version number they understand (i.e., 1 in the first clients), this can help make the upgrade transition to the newer version of the protocol easier. Instead of simply failing mysteriously, the client can at least tell the user that she should upgrade to a newer version. Most protocol designers hope they never have to make a change so drastic that it breaks compatibility, but should you find yourself in this situation, the txtvers key can help make the transition go a little more smoothly.

To register a service with TXT record attributes, you need to use the longer version of DNSSD.register( ) with the additional parameters:


DNSSDRegistration r = DNSSD.register(0, DNSSD.ALL_INTERFACES,
  name, "_example._tcp", null,  // Name, type, and domain
  null, port,                   // Target host and port
  txtRecord, this);             // TXT record and listener object

If you compare this new usage of register( ) with the one presented in the section "Registering a Service," you will note that there are several extra parameters, and that most of them have the value zero or NULL to signify that DNS-SD should use sensible default values.

Example 8-4 shows a complete listing for registering a service with added TXT record attributes. The change compared to Example 8-1 is indicated by the comment, "New code to register with TXT record begins here." In this example, as in Example 8-1, the program just waits for 30 seconds doing nothing, then calls b.stop( ) and exits.

Example 8-4. Java program to advertise a service with TXT record attributes

import java.net.*;
import com.apple.dnssd.*;

class TestRegisterWithAttributes implements RegisterListener
  {
  // Display error message on failure
  public void operationFailed(DNSSDService service, int errorCode)
    {
    System.out.println("Registration failed " + errorCode);
    }

  // Display registered name on success
  public void serviceRegistered(DNSSDRegistration registration, int flags,
    String serviceName, String regType, String domain)
    {
    System.out.println("Registered Name  : " + serviceName);
    System.out.println("           Type  : " + regType);
    System.out.println("           Domain: " + domain);
    }

  // Do the registration
  public TestRegisterWithAttributes(String name, int port)
    throws DNSSDException, InterruptedException
    {
    System.out.println("Registration Starting");
    System.out.println("Requested Name: " + name);
    System.out.println("          Port: " + port);

    // New code to register with TXT record begins here
    TXTRecord txtRecord = new TXTRecord(  );
    txtRecord.set("txtvers", "1");
    txtRecord.set("status", "ready");
    txtRecord.set("difficulty", "medium");
    DNSSDRegistration r = DNSSD.register(0, DNSSD.ALL_INTERFACES,
      name, "_example._tcp", null,  // Name, type, and domain
      null, port,                   // Target host and port
      txtRecord, this);             // TXT record and listener object
    // New code to register with TXT record ends

    Thread.sleep(30000);  // Wait thirty seconds, then exit
    System.out.println("Registration Stopping");
    r.stop(  );
    }

  public static void main(String[] args)
    {
    if (args.length > 1)
      {
      System.out.println("Usage: java TestRegisterWithAttributes name");
      System.exit(-1);
      }
    else
      {
      try
        {
        // If name specified, use it, else use default name
        String name = (args.length > 0) ? args[0] : null;
        // Let system allocate us an available port to listen on
        ServerSocket s = new ServerSocket(0);
        new TestRegisterWithAttributes(name, s.getLocalPort(  ));
        }
      catch(Exception e)
        {
        e.printStackTrace(  );
        System.exit(-1);
        }
      }
    }
  }

After you've compiled TestRegisterWithAttributes, we'll demonstrate it using our TestBrowse program. In one terminal window, run:


% java TestBrowse

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


% java TestRegisterWithAttributes "My Chosen Name"

In the TestBrowse window, you should see the service added. Now, while TestRegisterWithAttributes is still running, run TestResolve in a third terminal window:


% java TestResolve "My Chosen Name" local
TestResolve Starting
Service Resolved: mymac.local.:52658
Flags: 0, ifIndex: 5, FQDN: My\032Chosen\032Name._example._tcp.local.
        txtvers=1
        status=ready
        difficulty=medium
TestResolve Running
TestResolve Stopping

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

Next Pagearrow