NetNewsWire 3.2b13 is Out

August 4th, 2009

NetNewsWire3.2.jpg

There's a new beta for NetNewsWire 3.2 (3.2b13) and it fixes several bugs while working it's way to a complete feature set. Brent S. has said that this is an intermediate version on the way to 4.0 and adds the syncing with Google Reader. At this time, clippings are back but they don't sync - he's working on a solution.

New icon as well, by Rogue Sheep. Nice. Still a favorite way to get up to speed on what's going on each day.

Firefox 3.5.2 is Out

August 4th, 2009

Firefox3.5.jpg

Once again the Firefox team has shipped a "stability and security" update 3.5.2 to patch some holes in the browser. Being a popular browser that's now been downloaded 1,000,000,000 times (lifetime), makes one a tempting target for hackers, and that's the reason for the updates. No big deal, the restart is handled well.

Goin’ Old School on Some Code

August 3rd, 2009

Professor.jpg

Today I was trying to figure out why an app I inherited was returning wild values and I came across code that I'd skimmed several times before, but hadn't really dug into. Since the running program wasn't generating any exceptions, I assumed that the problem had to be in the data we were receiving. I spent several hours digging into the data, logging it, checking for problems, all to come to the conclusion that it couldn't be the data, and had to be something in how it was being interpreted in the code.

So back to the processing code that was the first step in the program.

My goals were simple - put in logging and try/catch blocks to make sure that we caught the exceptions and group the processing into distinct groups: gathering data, converting it into values, and then organizing the data into objects. Seems simple. And then I looked, really looked at the code.

What I saw made me shake my head... and bring out the "Old School" in me. The original code looked something like this:

  public uploadData(StringTokenizer mainTokenizer) {
    // get the number of rows as a rough size for the maps
    int size = mainTokenizer.countTokens();
	// ...make some maps to store data
 
    while (mainTokenizer.hasMoreTokens()) {
      String row = mainTokenizer.nextToken();
      StringTokenizer columns = new StringTokenizer(row, "\t");
      while (columns.hasMoreTokens()) {
        String nameString = columns.nextToken().trim();
        String dateString = columns.nextToken().trim();
        String costString = columns.nextToken().trim();
        String qtyString = columns.nextToken().trim();
        // ...more of the same
 
        String name = nameString;
        Date transDate = dateFormat.parse(dateString);
        Double cost = Double.valueOf(costString);
        Double qty = Double.valueOf(qtyString);
        // ...more of the same
 
        Order ord = orderFactory.createOrder(name, transDate, cost, qty);
        orderMap.put(name, ord);
        // ...more of the same
      }
    }
  }

The goal was admirable - divide the processing into three sections: acquisition, conversion, and organization. It's an admirable goal. But doing it this way is not only a little redundant, it's downright dangerous.

Look at the most obvious things: reused variables. Get rid of them. Make it cleaner by using the variables where they are needed. No more, no less. it's an admirable goal, and a good design plan, but when you're processing 30 or more variables in a table of data, it gets to be a little much when you have to repeat these three times. Trim it a little and keep it readable.

But he subtle things really are the danger signs. Why is he using the StringTokenizer for the rows in the table and not checking the number of elements? There's the possibility for a major confusion here. What if you have a row of data that's malformed? There's nothing that's going to tell me what the problem was and where to look in the data stream to correct it. Also, if there's an exception thrown all the remaining data is lost. Not a good thing.

Let's try something a little different.

  public uploadData(StringTokenizer mainTokenizer) {
    // get the number of rows as a rough size for the maps
    int size = mainTokenizer.countTokens();
	// ...make some maps to store data
 
     // processing variables used in the loop
    String name = null;
    Date transDate = null;
    Double cost = null;
    Double qty = null;
 
    // keep track of what I've done for logging at the end
    int    uploadCount = 0;
    int    uploadErrors = 0;
    while (mainTokenizer.hasMoreTokens()) {
      String row = mainTokenizer.nextToken();
      String[] columns = row.split("\t");
      if ((columns == null) || (columns.length != SIZE)) {
        log.error("No columns or wrong number.");
        ++uploadCount;
        ++uploadErrors;
        continue;
      }
 
      String nameString = columns[0].trim();
      String dateString = columns[1].trim();
      String costString = columns[2].trim();
      String qtyString = columns[3].trim();
      // ...more of the same
 
      try {
        name = nameString;
        transDate = dateFormat.parse(dateString);
        cost = Double.valueOf(costString);
        qty = Double.valueOf(qtyString);
        // ...more of the same
      } catch (NumberFormatException nfe) {
        log.error("While parsing " + name + " got a "
            + nfe.getMessage());
        ++uploadCount;
        ++uploadErrors;
        continue;
      } catch (ParseException pe) {
        log.error("While parsing " + name + " got a "
            + nfe.getMessage());
        ++uploadCount;
        ++uploadErrors;
        continue;
      }
 
      try {
        Order ord = orderFactory.createOrder(name, transDate, cost, qty);
        orderMap.put(name, ord);
        // ...more of the same
      } catch (Exception e) {
        log.error("While organizing " + name + " got a "
            + e.getMessage());
        ++uploadCount;
        ++uploadErrors;
        continue;
      }
 
      // update the count of rows processed
      ++uploadCount;
    }
 
    // log what we've done
    log.info("Processed " + uploadCount + " with "
        + uploadErrors + " errors");
  }

Now the logging messages are far too simplistic here, but they're where you need to log what's happening. First, forget that StringTokenizer and it's loop. What if you're wrong about the count? Use a simple String[] and then check the count. To maintain the style, you can leave the intermediate values, but another scheme is to have static final int values for each column header and then use them as opposed to the temporary variables:

      String nameString = columns[0].trim();
      String dateString = columns[1].trim();
      String costString = columns[2].trim();
      String qtyString = columns[3].trim();
      // ...more of the same
 
      try {
        name = nameString;
        transDate = dateFormat.parse(dateString);
        cost = Double.valueOf(costString);
        qty = Double.valueOf(qtyString);
        // ...more of the same
      } catch (NumberFormatException nfe) {
        log.error("While parsing " + name + " got a "
            + nfe.getMessage());
        ++uploadCount;
        ++uploadErrors;
        continue;
      } catch (ParseException pe) {
        log.error("While parsing " + name + " got a "
            + nfe.getMessage());
        ++uploadCount;
        ++uploadErrors;
        continue;
      }

becomes:

      try {
        name = columns[NAME_COL].trim();
        transDate = dateFormat.parse(columns[DATE_COL].trim());
        cost = Double.valueOf(columns[COST_COL].trim());
        qty = Double.valueOf(columns[QTY_COL].trim());
        // ...more of the same
      } catch (NumberFormatException nfe) {
        log.error("While parsing " + name + " got a "
            + nfe.getMessage());
        ++uploadCount;
        ++uploadErrors;
        continue;
      } catch (ParseException pe) {
        log.error("While parsing " + name + " got a "
            + nfe.getMessage());
        ++uploadCount;
        ++uploadErrors;
        continue;
      }

and what's left is as readable as the original, but uses half the variables. This is important if we're in a tight loop and getting hit with a ton of data. Garbage Collection is a killer for Java apps, and the fewer variables you can use the better.

When I put this code into the system I had an immediate hit: the data coming from the other app was leaving a column field empty when it meant to send a zero. I saw this with the NumberFormatException and it clearly labeled the row in the table where the error occurred. I looked in the multi-thousand line upload and sure enough - a blank. Given that it was meant to be a zero, I just put that into the code on the uploader so I wouldn't have to worry about other such occurrences. Easy.

So what have we learned?

Well... I've learned that it's better to have less code that does the same thing so long as it's readable and well commented. Both of these attributes were missing in the original version. Secondly, check everything - there's no reason not to, it'll save your bacon more times than you can imagine. Third, care about the code you're writing. It's a craft, after all. Be proud of it.

Colloquy 2.2.2 is Out

August 3rd, 2009

Colloquy.jpg

This morning I saw that Colloquy 2.2.2 was released with a required update that you get to Tiger (which is still pretty old) to use. I'm betting there are a lot of little bug fixes - probably a few crashing bug fixes, as the app is pretty darn stable as-is. If you want to have an IRC client, this is the best I've ever seen on the Mac.

Something to Really Like in Java – VarArgs and Arrays

July 31st, 2009

java-logo-thumb.png

I've been so jaded about Java that the last few years I really haven't worked to keep up with all the syntactical candy that has been added. The new for loops are a great example - as are templates: things that are in other languages that reduce typing a bit but don't really add to the power of the language.

So imagine my surprise when I started digging into varArgs in Java. First, I could see how they were used by a method:

  public void printAll(Object... things) {
    for (Object thing : things) {
      System.out.println(thing + ", ");
    }
  }

that's a pretty standard take on the C/C++ varArgs, and I'll admit, it's really nice to have. Makes the signatures of a lot of classes a lot simpler. But I needed to be able to call something with varArgs based on a stack.

For example, I have a parser that calls my code with all the arguments on a stack. I need to pop off the values - LIFO order, and then call the String.format() method with the fmt and then a bunch of Object values. But how to do that?

Thankfully, the Java guys really extended the language here. I'm really glad. They made the idea of Object... synonymous with the array: Object[]. This means that I can put all the values in an Object[] and then call the method, like this:

  /**
   * Get the format and all the args and then generate the string
   */
  public void run(Stack aStack) throws ParseException
  {
    // Check if stack is null
    if (aStack == null) {
      throw new ParseException("Stack argument null");
    }
    // see if we have a reasonable number of arguments
    if (curNumberOfParameters < 1) {
      throw new ParseException("You need at least a string format for "
            + "this call.");
    }
 
    // create an array of the proper size to hold all the args
    Object[]  varArgs = new Object[curNumberOfParameters - 1];
    if (varArgs == null) {
      throw new ParseException("I was unable to create an array to hold "
            + "the arguments. Check on it.");
    }
    // get the parameter from the stack
    for (int i = (curNumberOfParameters - 2); i >= 0; --i) {
      varArgs[i] = aStack.pop();
    }
    // finally, get the string format to use with these args
    String    fmt = (String) aStack.pop();
    // now we need to do the work... it will auto-convert the array
    aStack.push(String.format(fmt, varArgs));
  }

Needless to say, this is making the method within the parser very powerful. I now have the complete printf-like formatting available to my expressions, and while my original code worked (assuming a maximum size), this is far more flexible, and a far far better solution.

So I'm going to lighten up on Java for a while. Someone is still working on it in good ways that are just syntactic sugar.

VLC 1.0.1 is Out

July 31st, 2009

VLC.jpg

This morning I saw that VLC 1.0.1 is out, but interestingly enough, it wasn't available from the "Check for Updates" within VLC 1.0. Odd, but maybe they think this is a minor update and they didn't create a package to be downloaded/updated the other way. Seems reasonable, but I still wish they had release notes, or at least made them easier to find. I just couldn't find them on the site. Very odd for an Open Source project.

Anyway, got it so that I can keep ripping DVDs when I want to see them on my iPhone.

CoRD – a Cocoa Remote Resktop Client

July 30th, 2009

CoRD.jpg

I'm currently working in a tri-OS world. Lots of developers are using Mac laptops, linux workstations and Windows workstations. Things run on all these, and we need to be able to get to one from another. We have Cisco VPN, but I use Shimo for VPN control as it's so much nicer. But one of the things I've been concerned about is the ability to use RemoteDesktop into my Windows boxes (I need to monitor/control about 11 every day) from my Mac.

Enter CoRD 0.5.0.

It's an Open Source project on Sourceforge that places a Cocoa client to the Windows RemoteDesktop API and gives me the ability to show windows displays back on my Mac. That's something really nice. I can't wait to try it out.

Back in the Saddle (Again)

July 30th, 2009

OK, things are looking a lot smoother this morning. I've been able to get everything I needed from TimeMachine, the laptop is running well, it's got another 180GB of storage (up to 500GB from 320GB) which is always helpful, and things are coming back into a comfortable swing.

In this experience, I have to say the surprising moments were these:

  • The Apple Store had no drives. Why on earth not?
  • The drive was only $129 at Fry's. Again, so inexpensive, why not have a few on hand, Apple?
  • The replacement of the drive was easy. Thanks, Apple. It could have been a lot worse.
  • The original drive was a lemon because it didn't even last a year. Sad, but it happens.

So things are kicking right along. I've got a lot of little things to watch today and it should be a nice, easy day.

It's good to be back in the saddle.

Updated WordPress at HostMonster to 2.8.2

July 30th, 2009

wordpress.gif

When I was rebuilding my laptop, I started Firefox, like I always do, to open up a set of pages to HostMonster so that I can see what is going on there with all the sites I host there. Well... I noticed that WordPress 2.8.2 was out, and so I went to the SimpleScripts console at HostMonster and upgraded all my installs to 2.8.2.

The SimpleScripts install really is just about as easy as it can be. There's never a problem with the updates, everything is saved, they are fast, close to the release dates of the packages. Really, it's far better than the other package installer I used at HostMonster. Nice.

Growl 1.1.6 is Out

July 30th, 2009

growlicon.png

During the rebuild of my laptop this morning I got another update notification - this one was from Growl for 1.1.6. This one even had release notes! This is the messaging system that a lot of Mac software uses, and I have to say, when I first saw it I thought "Why?", but now that I'm using my Mac as everything, it makes a lot of sense. Get the notifications you want to see as you want to see them. Nice.

Anyway, the fixes look like they will help, and with Snow Leopard on the way, it's nice to see these packages getting updated.