oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

A Bioinformatics Web Service with Mac OS X, Part 2
Using Objective-C and Mac OS X's Core Web Services to Construct an OmniGene Analysis Engine Client--Part Two

by Brian Gilman

In last week's article, I introduced the OmniGene Analysis Engine (OAE), demonstrated how to build Objective-C stubs using the WSMakeStubs utility, and showed how to integrate the autogenerated files into a project using Project Builder and wire up the graphical user interface for the OAE client application.

This week, we'll finish up the project and I'll show you how to write the Objective-C code to execute the web service, respond to user input, and populate the widgets that you dropped onto the graphical user interface.

Writing The Cocoa Code

Now we're at the point where we can write the code that connects up all of these objects to our web service. Click on AnalysisController.h and add the following instance variables highlighted in bold text:

	@interface AnalysisController : NSObject
    IBOutlet id dropDown;
    IBOutlet id tasksButton;
    IBOutlet id textField;
    NSArray *tasks;
    NSDictionary *taskInfo;
- (IBAction)clear:(id)sender;
- (IBAction)getTasks:(id)sender;
- (IBAction)sumbitJobs:(id)sender;

Next, we'll add some code to AnalysisController.m to get the Submit Tasks button to respond to user clicks. We want the application to respond to a mouse click on the Get Tasks button by:

  • Calling our web service.
  • Getting the names of the services.
  • Filling in the drop-down menu.
  • Populating the table view.

Let's concentrate on the first three behaviors. We will visit the fourth a little later in this article.

Calling the Web Service

I'm going to briefly discuss the interactions that are going to take place before delving into more code. This should help you follow along as we build the application. Figure 1 shows the interactions and data that are getting sent back and forth between our OAE client and OAE server:

Figure 1. Sequence diagram for our AnalysisApp web service client.

Our AnalysisService stub defines methods to get the tasks that are registered with the OmniGene Analysis Engine. We need to get an instance of this stub by calling the AnalysisService convenience method getTasks. We will call this method inside of our controller class, which contains a method with the same name. This method will get called when the user clicks on the Get Tasks button.

Coding the getTasks Method in AnalysisController

Start by adding the following code to the getTasks method of our AnalysisController.m file:

tasks = (NSArray*)[AnalysisService getTasks];

Notice that the getTasks method returns an NSArray pointer. This array holds pointers to NSDictionary objects that are filled with the information returned by the OmniGene Analysis Engine. These pieces of information are:

  • The IDs of the tasks associated with this analysis service.
  • The names of the tasks.
  • The parameter information that you need to pass to these tasks encoded as Objective-C objects.
  • An XML segment that represents the parameter information.
  • A textual description of the analysis task.
  • The Java class name that has been registered with the Analysis Engine.

To reiterate, each index of the NSArray tasks corresponds to an algorithm that is registered with the OAE, and each NSDictionary that is contained in the tasks array holds more detailed information about the service.

We need to get information out of the tasks array to populate our table view and drop-down menu. We will iterate through the NSArray returned by the getTasks method and then traverse the dictionary asking for this information by name. We do this by calling objectForKey: key on NSDictionary.

The code you need to add is in bold text below:

- (IBAction)getTasks:(id)sender
    tasks = (NSArray*)[AnalysisService getTasks];
    int index;
    for(index =0; index< [tasks count]; index++){
	taskInfo = (NSDictionary*)[tasks objectAtIndex:index];
    	NSString *name = [taskInfo objectForKey: @"name"];

Here we are iterating through the tasks array, which represents a list of tasks (algorithms) that are available in the OmniGene Analysis Engine. We then get the name of the task by traversing the dictionary ("hashtable," for Java and C++ folks in the audience) that is stored in the tasks array and then asking the dictionary for a key named "name" for its associated value. This gives us back the name of each algorithm that is registered with OAE. We will populate the drop-down menu with this information and then update the table view with the details. To do this, add the code that is in bold below to the getTasks method of AnalysisController.m.

- (IBAction)getTasks:(id)sender
    [dropDown removeAllItems];
    tasks = (NSArray*)[AnalysisService getTasks];
    int index;
    for(index =0; index< [tasks count]; index++){
	taskInfo = (NSDictionary*)[tasks objectAtIndex:index];
    	NSString *name = [taskInfo objectForKey: @"name"];
	[dropDown addItemWithTitle: name];

	[textField reloadData];

Here we clear the drop-down menu before calling the web service. We do this because we do not want to keep populating the drop-down with the same items each time the user clicks on the Get Tasks button. The OAE also allows users to dynamically register algorithms, so we clear the list and then call the web service to refresh the drop-down with any new services that have been registered with the engine after the last click on the Get Tasks button.

The method addItemWithTitle adds the names of the algorithms to the drop-down menu.

Finally, we tell our table view object (called textField) to reload any detail information. The method reloadData tells our table view to call the following two (and final) methods of our class:

- (int)numberOfRowsInTableView:(NSTableView *)tableView
    if(tasks == NULL){
        return 0;
    return [tasks count];

-(id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
    id theValue;
    NSDictionary* info = (NSDictionary*)[tasks objectAtIndex: rowIndex];
    theValue = [info objectForKey:[aTableColumn identifier]];
    return theValue;

The numberOfRowsInTableView:(NSTableView *)tableView method tells our table view how many rows there are to display (in our case, one; the echo task is the only service you have registered in OAE).

The tableView:objectValueForTableColumn:row: method is then called by our table view object when reload data is called above to display column specific information. This method gets the detailed task information from the information stored in the tasks array. The is accomplished through the following code block:

NSDictionary* info = (NSDictionary*)[tasks objectAtIndex: rowIndex];

The info dictionary holds detailed task information. The row index corresponds to the number of rows that were returned by the numberOfRowsInTableView:(NSTableView *)tableView method.

Next, the table view's columns are populated with information found in the dictionary that we pulled out of the tasks array. All of this information is gathered by passing the identifier for each column that we set up in interface builder to the dictionary object stored in the tasks array. This is accomplished using:

theValue = [info objectForKey:[aTableColumn identifier]];

Finally, the object stored in theValue (an NSString containing detailed service information as described above) will be used to populate each row of the table.

You're finished! Compile and run the application, click on the Get Tasks button, and you will see the tasks that are registered with the OAE appear in the drop-down menu and in the table view. You should see something like what is shown in figure 2:

Screen shot.
Figure 2. The Cocoa view populated with OAE information.

After clicking on the Get Tasks button a few hundred times, you will find that this application is not very interesting. To continue with this project, the next steps will be to submit jobs to the AOE and gather information back from a Blast analysis service by implementing the Submit Job button handler and filling in more of the tab pane shown in figure 2.

Brian Gilman is a Group Leader in the Medical And Population Genetics Department at the Whitehead Institute Center For Genome Research.

Return to the Mac DevCenter.