Monday, February 06, 2006

A Breakthrough!

I've had a breakthrough! I woke up this morning with the realization that part of my (code) writer's block is centered on my (current) inability to decompose the problem into a set of objects and methods, the root of OO design. Usually I'm very good at this. It's the way I've always worked and it's never failed me (this badly) before. Anyway, my breakthrough is "screw the design!" There are certain things the program is going to need to do. I don't know what all of them are but I do know some. For example, DNS queries are going to be critical to the program. How and where it fits into the big scheme of things is unclear at the moment I know it's needs to be in there somewhere. So I've created a class called Whiteboard where I can temporarily stick some of this stuff until I find a permanent home for them.

More About DNS Queries

Normally if the Java standard library has the ability to doing something for which there exists external libraries I will use the standard library even if the standard library's approach is inelegant, so long as there isn't a performance reason not to. Today I've arrived at such an junction.

In the standard library, if you want to issue DNS queries for anything besides A and PTR records, you must use JNDI. JNDI was really designed for use with LDAP but Sun managed to shoehorn some DNS into it. On the other hand there is dnsjava. An open-source java library for issuing all types of DNS queries.

Say I wanted to lookup the MX records for blogger.com. Using JNDI it would look like this:

String[] MX_RECORD = {"MX"};

/**
 * Returns the MX records for domain sorted by preference or
 * null if no MX records where found.
 *
 * @param domain The domain name.
 *
 * @return The MX records for domain.
 *
 * @throws NamingException
 */
public List getMX( String domain ) throws NamingException
{
    DirContext ctx = new InitialDirContext();
    try
    {
        String query = "dns:/".concat( domain );
        Attribute att = ctx.getAttributes( query, MX_RECORD ).get( "MX" );
        if( null == att )
            return null;

        Collection sorter = new TreeSet();
        for( Enumeration e = att.getAll(); e.hasMoreElements(); )
            sorter.add( ((String)e.nextElement()).trim() );

        List sorted = new ArrayList( sorter.size() );

        for( String mx : sorter )
        {
            /* Strip off preference integer. */
            int offset = 0;
            int len = mx.length();
            
            for( char c;
                 offset < len
                 && (Character.isDigit( c = mx.charAt( offset ) )
                    || Character.isWhitespace( c )); )
                 offset++;
     
            /* Strip trailing dot (.) if it exists. */
            if( '.' == mx.charAt( len - 1 ) )
                len -= 1;
            mx = mx.substring( offset, len );

            sorted.add( mx );
        }
        return sorted;
    }
    finally
    {
        try
        {
            ctx.close();
        }
        catch( NamingException ignore )
        {
        }
    }
}

Doing the same with dnsjava it would look like this:

static Comparator MX_SORTER = new Comparator()
{
    public int compare( Object r1, Object r2 )
    {
        return ((MXRecord)r1).getPriority() - ((MXRecord)r2).getPriority();
    }
};

/**
 * Returns the MX records for domain sorted by preference or
 * null if no MX records where found.
 *
 * @param domain The domain name.
 *
 * @return The MX records for domain.
 */
public static List getMX( String domain )
{
    try
    {
        Record[] records = new Lookup( domain, Type.MX ).run();
        if( null == records )
            return null;
        
        Arrays.sort( records, MX_SORTER );
        List mx = new ArrayList( records.length );
        
        for( Record record : records )
        {
            String host = ((MXRecord)record).getTarget().toString();
            int len = host.length();
            if( '.' == host.charAt( len - 1 ) )
                host = host.substring( 0, len - 1 );
            mx.add( host );
        }        
        
        return mx;
    }
    catch( TextParseException e )
    {
        throw new RuntimeException( e );
    }
}

I'm praying that dnsjava outpeforms JNDI. I'm off to perform some micro benchmarks. I'll let you know how it went.

2 comments:

  1. Anonymous1:14 PM

    What about anti-patterns and not using design patterns at all. Most patterns are not concerned on performance but on decoupling and cohesion.

    ReplyDelete
  2. Pedro, I have no idea what your comment is refering to.

    ReplyDelete