oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Integrating Xgrid into Cocoa Applications, Part 2

by Drew McCormack

In Part 1 of this two-part extravaganza we covered a lot of ground, with barely a mention of Cocoa. We were witness to a future vision for Xgrid, installed its present incarnation, played with it a bit, and then got down and dirty on the command line. The title would suggest that there may actually be some Cocoa in these pages, and I am here to assure you that it wasn't all just a PR stunt to capture your attention. I will now deliver on the promise.

Photo Industry

In this part of our journey into Xgrid, we're going to develop a little Cocoa application called Photo Industry. This will be an Xgrid-enabled app, and what's more, it will be a standalone application, not an Xgrid client plugin. To achieve this goal, we'll leverage the xgrid command-line tool using a Cocoa class called NSTask. I hope that in the near future, perhaps as early as WWDC, Apple will make this technique obsolete by publishing an Xgrid client Cocoa API, but until that time we can at least appreciate the potential of Xgrid by wrapping the xgrid tool.

Photo Industry, an Xgrid-enabled JPEG image filtering application.

Photo Industry: an Xgrid-enabled JPEG image filtering application. Baby courtesy of proud parents.

Photo Industry is a JPEG image filtering program. The best way to see what it does is to download the finished product, and try it out. Here is how:

  1. Make sure you are running Mac OS X 10.3.x. Photo Industry will not work on earlier versions of Mac OS X.
  2. Download the Photo Industry app here, and put it somewhere on your hard disk. (By the way, if you are wondering who created the swish icon, you need look no further than Bobby is a young lad with a lot of talent, and will create polished icons for you at a fraction of the price of the big boys.)
  3. Start it up by double clicking.
  4. Start an Xgrid controller on your computer in the System Preferences panel, and start agents on one or more computers attached to your LAN or WLAN.
  5. In Photo Industry, select one or more filters from the table shown.
  6. In Finder, select a number of JPEG images (say <10), and drag them to the top image view in Photo Industry. WARNING: PLEASE DON'T USE YOUR SOLE COPY OF AUNTY JOAN'S WEDDING SNAPS. MAKE A COPY, OR USE IMAGES THAT YOU DON'T MIND LOSING SHOULD ANYTHING GO WRONG.
  7. When presented with an Open sheet, select a directory where you would like to have the output images end up.
  8. Wait. You can monitor progress on the progress bar, with the timer, and from the display of processed images that appear in the lower image view.
  9. If all goes well, your processed images should be in the output directory upon completion.

Play around with the app for a few minutes. See what happens to the processing time when you add an agent to your Xgrid "cluster" or take one away. And when you are ready, we can start looking at how it all works.

Wrapping xgrid in Cocoa

To get started, download the Xcode project and source code here. Unpack it, and open it in Xcode.

The most generic class in this project is called DistributedTask. It is the class that wraps the xgrid tool, providing its functionality to the rest of the Cocoa source. You could probably use this class directly in your own projects without any modification at all.

DistributedTask is really nothing more than an Xgrid class, and should an Apple Xgrid API ever appear, I would expect something very similar to DistributedTask to be in it. The name has been inspired by similarities with Cocoa's NSTask: DistributedTask is basically the same as NSTask, but runs a series of commands on a distributed Xgrid network, rather than just a single command on the local machine.

However, the name could cause some confusion, so let's clarify things before we start. What we call a distributed task in Photo Industry, roughly corresponds to a job in Xgrid. A distributed task is made up of a number of subtasks, which are approximately the same as tasks in Xgrid terminology. Hopefully I've made that confusing enough for you.

The DistributedTask class itself is pretty straightforward, though it does involve multithreading. Here is the public interface:

#import <Foundation/Foundation.h>

@class DistributedTask;

@interface NSObject (DistributedTaskDelegateMethods)
-(void)distributedTaskDidLaunchSubTasks:(DistributedTask *)distributedTask;
-(void)distributedTask:(DistributedTask *)distributedTask 
-(void)distributedTaskDidFinishSubTasks:(DistributedTask *)distributedTask;

@interface DistributedTask : NSObject {
    NSString *controllerURLString;
    NSMutableDictionary *subTasks;
    NSMutableSet *subTasksRunning;
    BOOL taskHasLaunched;
    id delegate;

-(id)initWithControllerURLString:(NSString *)controllerURLString;



-(NSString *)controllerURLString;

    launchPath:(NSString *)launchPath
    workingDirectoryPath:(NSString *)workingDirPath
    outputDirectoryPath:(NSString *)outputDirPath
    standardInputPath:(NSString *)standardInputPath
    standardOutputPath:(NSString *)standardOutputPath;


As you can see, the class makes use of a delegate, which is a common technique in Cocoa. The delegate is registered to receive information about the distributed task when certain events occur. In this case, a delegate message is sent when the distributed task is launched, when one of its subtasks completes, and when all subtasks have finished.

The class interface block includes a number of attributes that are important for the implementation, and we will discuss these as they come up.

The DistributedTask initializer takes a single argument, namely the Xgrid controller to which it should connect. We just use localhost in Photo Industry, but you could very easily implement a more complicated controller location scheme involving Rendezvous.

The delegate accessor methods are also present in the interface, along with an accessor for the controller URL. Note that there is only a getter for the URL; you can't change the controller after you have initialized the class object, so if later you want to use a different controller, you need to create a new DistributedTask.

Two more rather important methods follow. The first is the method you use to add subtasks to the distributed task. It takes a number of arguments, such as the path to the command to launch; working directory path; output directory path; and standard input and output paths. You may recognize these arguments as corresponding to the command-line arguments of the xgrid command. That's no coincidence, because DistributedTask is really nothing more than a Cocoa xgrid command.

The last method, launch is pretty self explanatory. "Launch" is used instead of "run," or another apt verb, in an attempt at consistency with the terminology used in NSTask.

The implementation file of DistributedTask begins by declaring some private methods, and a number of strings.

@interface DistributedTask (PrivateMethods)
-(void)runSubTaskAsynchronouslyWithDictionary:(NSDictionary *)subTaskDict;
-(void)subTaskDidFinishWithDictionary:(NSDictionary *)subTaskDict;

// SubTask dictionary keys
static NSString *SubTaskIdKey               = @"SubTaskIdKey";
static NSString *LaunchPathKey              = @"LaunchPathKey";
static NSString *OutputDirectoryKey         = @"OutputDirectoryKey";
static NSString *WorkingDirectoryKey        = @"WorkingDirectoryKey";
static NSString *StandardInputKey           = @"StandardInputKey";
static NSString *StandardOutputKey          = @"StandardOutputKey";

We'll address the private methods below. The strings are all keys for a dictionary. Rather than defining a separate SubTask class, I have opted to simply use dictionaries to store information pertaining to each subtask. Defining the keys like this reduces the chances of making a spelling error in a string, which would result in a run-time bug, and also makes explicit what entries a subtask dictionary contains. It is really very similar to defining a struct in C; after all, a dictionary is really no more than a dynamic struct.

The implementation proper begins with the initializer and deallocator.

@implementation DistributedTask

-(id)initWithControllerURLString:(NSString *)url {
    if ( self = [super init] ) {
        controllerURLString = [url copy];
        subTasks = [[NSMutableDictionary alloc] initWithCapacity:10];
        subTasksRunning = [[NSMutableSet alloc] initWithCapacity:10];
        taskHasLaunched = NO;
        delegate = nil;
    return self;

-(void)dealloc {
    // Remove task directory   
    [controllerURLString release];
    [subTasks release];
    [subTasksRunning release];
    [super dealloc];

Pages: 1, 2, 3, 4, 5

Next Pagearrow