oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Integrating Xgrid into Cocoa Applications, Part 2
Pages: 1, 2, 3, 4, 5

We can now finish off the applyFilters:toFilesWithPaths:forOutputDirectoryPath: method.

        // Copy PIL for the input directory
        NSBundle *bundle = [NSBundle bundleForClass:[self class]];
        NSString *pilPath = [bundle pathForResource:@"PIL" ofType:nil];
        NSAssert( nil != pilPath, @"PIL path was nil." );
        [fm copyPath:pilPath toDirectoryAtPath:inputDirPath];
        // Add subtask to task
        NSString *scriptPath = 
            [bundle pathForResource:@"agentrunscript" ofType:@"py"];
        [task addSubTaskWithIdentifier:[NSNumber numberWithInt:subTaskIndex]
    [task launch];

Still inside the loop over subtasks, we copy the directory containing PIL to the input directory of the subtask. The subtask is then added to the distributed task, using the method discussed earlier, setting the launch path, input and output directories of the subtask, and the standard input file. We don't need the standard output, so nil is passed for that. Finally, when all subtasks have been added to the distributed task, the task is launched.

The progress of the DistributedTask is monitored using its delegate methods. The PIController was set as the delegate to the task, so it can implement the methods, and act upon them as required. We will take a look at the method called when the DistributedTask has finished running all its subtasks on Xgrid: distributedTaskDidFinishSubTasks:.

The method first checks that the output directory that the user requested exists. If not, it creates it. If it does exist, and is not a directory, an NSAssert ensures an exception is raised. In a more robust app, you would want to handle this better, by informing the user of the problem.

-(void)distributedTaskDidFinishSubTasks:(DistributedTask *)distributedTask {    
    NSFileManager *fm = [NSFileManager defaultManager];
    // Ensure output directory exists, and that it is a directory.
    BOOL isDir;
    if ( [fm fileExistsAtPath:[self outputDirectoryPath] isDirectory:&isDir] ) {
        NSAssert( isDir, @"Output directory path supplied was not a directory." );
    else {
        [fm createDirectoryAtPath:[self outputDirectoryPath] attributes:nil];

Next, the filtered photos, which should be in the output directory of the DistributedTask, are moved to the user's chosen output destination.

    // Move task output files to the output directory
    NSString *taskOutputDirPath = 
        [taskTempDirPath stringByAppendingPathComponent:@"output"];
    [fm changeCurrentDirectoryPath:taskOutputDirPath];
    NSDirectoryEnumerator *en = [fm enumeratorAtPath:taskOutputDirPath];
    NSString *relativePath; // Path relative to taskOutputDirPath
    while ( relativePath = [en nextObject] ) {
        if ( NSOrderedSame != 
                 [[relativePath pathExtension] caseInsensitiveCompare:@"jpg"] &&
             NSOrderedSame != 
                 [[relativePath pathExtension] caseInsensitiveCompare:@"jpeg"] ) 
        NSString *fileOutputPath = 
            [[self outputDirectoryPath] stringByAppendingPathComponent:
                 [relativePath lastPathComponent]];
        if ( [fm fileExistsAtPath:fileOutputPath] ) 
            [fm removeFileAtPath:fileOutputPath handler:nil];
        [fm linkPath:relativePath toPath:fileOutputPath handler:nil];

An NSDirectoryEnumerator is employed for this operation. An NSDirectoryEnumerator traverses the contents of a directory, including subdirectories. For each file found, we check if it is a JPEG, and, if so, link it to the output directory. Yes, in this case we do use link, instead of copy, because the operation doesn't pose any threat to the original photos, and is faster.

Finally, we clean up, by removing the entire task temporary directory, which includes all of the files and directories used by the subtasks.

    // Remove temporary directory
    [fm removeFileAtPath:taskTempDirPath handler:nil];

Odds and Ends

We have now covered the parts of Photo Industry that relate directly to Xgrid. If you download the source, you will see there is a lot of other stuff in the application, which could also be useful in your own apps. Photo Industry makes use of drag and drop, for example, and the new Cocoa bindings layer. A good intro to the drag-and-drop techniques can be found on CocoaDevCentral. Bindings are also covered by CocoaDevCentral here, and don't forget that old stalwart Mike Beam, who has recently written on the topic here.

I hope this two-part article has demonstrated the potential of Xgrid in non-scientific Cocoa applications. We have had to do a lot of work in order to leverage that potential, using the command-line xgrid tool, but in all likelihood WWDC will alleviate that in the near future. Hopefully, this article will be irrelevant after June 28, 2004, when SJ finally unveils Apple's vision for Xgrid and the future of distributed computation. To be continued ...

Drew McCormack works at the Free University in Amsterdam, and develops the Cocoa shareware Trade Strategist.

Return to the Mac DevCenter