Politics and Cats

I wondered a few days ago why the Greenbelt Metro Station doesn't see politicans like the Silver Spring Metro Station does. Well, this week, the politicians finally found the station. Pinsky and Mfume had representatives out on Tuesday afternoon (perhaps that was Pinsky himself?) and Jack Johnson had supporters out this morning.

Speaking of , don't touch that calico cat down the street. It seems nice and meows at you and starts to purr when you pet it, but then it gets all grumpy and scratches your hand and arm until people at work notice the marks on your wrist and wonder if maybe life hasn't been just a little too stressful for you lately and maybe it's time to pull back some?

ClearQuest SOA: Part III/The Producer

This is part 3 in a series about ClearQuest and SOA. If you're not a CQ geek, you'll want to skip this. If you're interested in playing along, go over to that first article and get set up. Or return to part 2 to catch up

At the end of this series, we will have created an Ant task that runs a ClearQuest query without installing ClearQuest on the client machine. Through this exercise, we will have explored very simple SOAification of ClearQuest.

Last time, we made some little Perl scripts that let us connect to ClearQuest and get the results of running a named query. This time, we're going to try to set up a website to do this. First, we'll set up Apache to allow us to run a CGI script to run a query. Then, we'll write a script that listens for, and responds with, XML.

CQWS - part 3

We will not expose the Perl modules we created last time to the web directly. Instead, we'll create scripts to act as intermediaries. These scripts are called CGI scripts. For convenience, let's create a directory under our webservice directory and call it cgi-bin. For me, that directory is:

C:\Perl\site\lib\CQWS\cgi-bin

In part 1, you should have installed an Apache web server. (Since we're dealing with ClearQuest, it is possible that you just want to use the Rational Web Platform (RWP). RWP is a stripped down Apache server, but it will work for this task. Except for the sentence after next, whereever I say 'Apache', you can substitute 'RWP'.) We'll want to make a change to Apache's configuration file so that it points to this cgi-bin directory for scripts. If you're using Apache, this is going to be a file called httpd.conf, but if you're using the Rational Web Platform, the file will be called rwp.conf. This file already exists for you in your installation directory. Find it and open it in your favorite editor.

We're looking to modify or create a line that points a ScriptAlias at our new cgi-bin directory. So, make the path match whereever you put your cgi-bin directory:

ScriptAlias /cgi-bin/ "C:/Perl/site/lib/CQWS/cgi-bin/"
Notice that you'll have to lean the directory slashes forward. Restart your web server and see if you can get it to work. You should get some sort of start-up page from pointing a web browser at http://localhost. If not, you'll need to figure out what is wrong with your configuration of Apache before proceeding.

Last time around, we created a quick test script called test.pl that, run from the command line, allowed us to verify that our modules connected to ClearQuest and returned data. To quickly test that we have set up our cgi-bin directory correctly, copy that test.pl script from your CQWS/tests directory into your CQWS/cgi-bin directory. We'll have to make a quick addition to make this script work inside of the web server. Make a new first line that points to the location of your Perl.exe program. Add a second line to make the print statements visible on the web. Like this:

#!C:/Perl/bin/Perl.exe
print "Content-type: text/plain; charset=iso-8859-1\n\n";
require("CQWS/query.pm");
my $queryList = query->get_query_names;

foreach ( @$queryList)
{
print $_ ."\n";
}

my $name = "Public Queries/All Defects";
print "Testing with " . $name . "\n";
my $queryResult = query->execute_named_query($name);
# -- loop through all the results and print out one column
foreach ( @$queryResult )
{
my $record = $_;
foreach ( sort keys %$record)
{
print "\tcolumn: $_ \tvalue: " . $record->{$_} . "\n";
}
}
Now, point your browser to http://localhost/cgi-bin/test.pl and it should run the Perl script. It might take a moment or two, if you have a lot of items in your query result. The output in the browser will look a lot like the output on your command line.

Congratulations, you've created a web application. You can visit this address any time you want to run that specific query and see its output in this text format. But for this to be useful, we need for the application to accept requests (e.g., run CQ query X) and respond with XML so that any sort of external application can use it.

We're going to use a technology called Remote Procedure Call (RPC). Basically, the consumer will post XML to our cgi script. The XML will include a method name and one or more parameters. For example, we might have the method name be "query.run" and the parameter would be the name of the query, like "Public Queries/All Defects". Then, our cgi script will return XML formatted in a way that clients know how to read.

This sounds a little daunting, but really all we have to do is steal from the internet, like all good Perl scripters do. We'll use the Frontier::RPC modules to allow us to understand incoming XML requests and to format our outgoing responses. Further, we'll steal and modify this script I found on the web.

Create a file in your cgi-bin directory called cqws.pl and copy the following bits into it. We're not going to modify any of the bottom stuff, we're only going to add to the top, so you don't really need to know how this part works. (It just talks RPC talk to Frontier to take the results of running our Perl modules and format them in such a way that an RPC client can understand them.)

#!C:/Perl/bin/Perl.exe

use strict;
use warnings;
use Frontier::RPC2;

sub mytest {
my $rs = "SUCCESS";
return $rs;
}

sub run_query {
require("cqws/query.pm");
return query->execute_named_query(@_);
}

process_cgi_call(
{
'query.test' => \&mytest,
'query.run' => \&run_query,
});

#==========================================================================
# CGI Support
#==========================================================================
# Simple CGI support for Frontier::RPC2. You can copy this into your CGI
# scripts verbatim, or you can package it into a library.
# (Based on xmlrpc_cgi.c by Eric Kidd .)

# Process a CGI call.
sub process_cgi_call ($) {
my ($methods) = @_;

# Get our CGI request information.
my $method = $ENV{'REQUEST_METHOD'};
$method = "undefined" if ( ! defined $method );
my $type = $ENV{'CONTENT_TYPE'};
my $length = $ENV{'CONTENT_LENGTH'};

# Perform some sanity checks.
if ( $method eq "POST" )
{
http_error(400, "Bad Request") unless $type eq "text/xml";
http_error(411, "Length Required") unless $length > 0;

# Fetch our body.
my $body;
my $count = read STDIN, $body, $length;
http_error(400, "Bad Request") unless $count == $length;

# Serve our request.
my $coder = Frontier::RPC2->new;
send_xml($coder->serve($body, $methods));
} elsif ( $method eq "GET" ) {
my $coder = Frontier::RPC2->new;
my $rs = $coder->encode_response("This is a response to a GET. Typically, we're going to be looking for a POST here.");
send_xml($rs);
} elsif ( $method eq "undefined") {
# --
# Probably run from the command line
# If they provide a filename with xml in it, we can
# see what would happen over the web with a method call
my $coder = Frontier::RPC2->new;
my $rs = $coder->encode_response("I do not understand your request");
if ( @ARGV eq 1 )
{
my $xfile = $ARGV[0];
open(XFILE,"$xfile") || die("Cannot open $xfile");
my @xinfo = <XFILE>;
close XFILE;
$rs = $coder->serve("@xinfo",$methods);
}
send_xml($rs);
} else {
http_error(405, "Method Not Allowed ($method)") ;

}
}

# Send an HTTP error and exit.
sub http_error ($$) {
my ($code, $message) = @_;
print <<"EOD";
Status: $code $message
Content-type: text/html

<title>$code $message</title>
<h1>$code $message</h1>
<p>Unexpected error processing XML-RPC request.</p>
EOD
exit 0;
}

# Send an XML document (but don't exit).
sub send_xml ($) {
my ($xml_string) = @_;
# --
# here's some weird stuff
# because the length doesn't include \n,
# but then the reader things the message is shorter
# by as many characters as there are hard returns
# --
my $length = length($xml_string) + split("\n",$xml_string);
print <<"EOD";
Status: 200 OK
Content-type: text/xml
Content-length: $length

EOD
# We want precise control over whitespace here.
print $xml_string;
}
It's the stuff in bold we're interested in. The first subroutine (mytest) simply responds with a string. The second subroutine (run_query) passes the input parameters along to the execute_named_query subroutine that we created last time. And finally, the process_cgi_call subroutine passes along a mapping of methods that the client might call (e.g., query.test) to the subroutines we defined (e.g., \&mytest).

Why not just pass the actual subroutines from the query.pm module instead of all this whacky redirection?

Partly because that's the way the script I stole came, and partly because it makes us more flexible in the future. I suspect we're going to find that the query.pm module is a nice generic module that we will want to use with both regular old Perl scripts and this fancy SOA thing. If so, we'll probably get to a point where we decide that we want our Perl scripts to be getting back a nice array, but our SOA consumers need the reference. In that case, we'll be able to modify this subroutine here to get whatever sort of response comes from the query.pm module and format it properly for response.

Even more likely, we're going to need how to figure out how to pass usernames and passwords for CQ around, so it's likely that many of our calls will come in with a reference to a user hash or some such. In that case, we'll be able to use the subroutines to pull that bit of info out and push just the query name over to the query subroutine. We'll still need to rearchitect some, but this is the proper approach to scale.

Now, whenever some consumer passes along a method request and parameters to the http://localhost/cgi-bin/cqws.pl address, the script should try to figure out what to do and pass XML back. You can try to run the script from the command line, but it won't know what to do because it's looking for a POST. However, you can see what its XML responses are going to look at. So, give it a try:

C:\Perl\site\lib\CQWS\cgi-bin>Perl cqws.pl

Status: 200 OK
Content-type: text/xml
Content-length: 162

<?xml version="1.0"?>
<methodResponse>
<params>
<param><value><string>I do not understand your request</string></value></param>
</params>
</methodResponse>
Status: 405 Method Not Allowed (undefined)
Content-type: text/html

<title>405 Method Not Allowed (undefined)</title>
<h1>405 Method Not Allowed (undefined)</h1>
<p>Unexpected error processing XML-RPC request.</p>

Congratulations! Now, you've created a producer. But how do you know it works? Guess you'll need something to consume that product, eh? Join us next time, when we'll wrap up by producing consumers.


All the parts in this series:

Little Miss Understanding?

Am I the only one who finds it ironic that the Old Greenbelt Theatre is playing Little Miss Sunshine in this week before the annual Labor Day Festival, which happens to feature the Little Miss Greenbelt pageant?

Help Crazy Guy On a Bike

Waaayy back in the day, the Brunette and I took a bike trip from DC to Minnesota. This was in the Spring/Summer of 2001, and it was a lovely trip, except, you know, for all the bike riding.

At any rate, it was back before we'd ever heard of blogs and Flickr was barely a gleam in some geek's eye. But I had a little PDA that I kept notes on, and it had a little dial-up modem, so every so often, we could log into the internet. This guy had a website where you could keep a journal of your trip, including a little mileage calculator. It was free and our friends and family could see that we safely made it from one town to the next.

They could also see how long it took us to get from one town to the next.

I guess you could call that journal the pre-blog. And it was free, too. Since then, I've lost touch with the site, but it seems to have become a little cyclist community, and I have fond memories of that trip. So, when I heard today that the guy who created the site had a bike accident but didn't have insurance to cover the recovery, it tugged at the ole heartstrings.

So, I'm passing the word on to you people. I don't know if there are any bikers who stop by, but this guy had a nice service back in the stone age. Give him a hand, if you can.

1:20/1:15

The bugs were out in force last night. They dove into my eyes and down my throat, but despite their efforts and the fall of night, I made it home in normal time yesterday. Sadly, Summer's coming to a close. The night is creeping nearer and the window of opportunity for biking is shrinking. I was in full dark from Route 1 to South Way, I think. Of course, I didn't get out of the office until 7:40.

This morning was pretty easy, but you can see that I'm quite the slow putzy rider. Once a week isn't enough for me to work on speed, only on surviving the trip. Also, in addition to the death of Summer, we are experiencing the birth of School. The yellow buses were out in force today. I hope the drivers get used to the sense of their vehicular boundaries soon. There were a couple of close calls.

But the important thing is that I'm safe and I could write some boring rambling thing for people who don't care about ClearQuest to read.

ClearQuest SOA: Part II/Foundation Modules

This is the second part of a series about ClearQuest and SOA. If you're not a CQ geek, you'll want to skip this. If you're interested in playing along, go over to that first article and get set up.

At the end of this series, we will have created an Ant task that runs a ClearQuest query without installing ClearQuest on the client machine. Through this exercise, we will have explored very simple SOAification of ClearQuest.

We're going to start with the easy stuff: Let's make a set of Perl modules that allow us to connect to ClearQuest and run a query. In a future episode, we'll make the web service that will use these modules to gather information. Later than that, we'll create a consumer for that production.

Now is the time for me to tell you that I don't really know what I'm doing, but I can't really hurt anything, right? I also want to mention that there are going to be some things that I over-complicate because I'm worried about expansion in the future, and there are going to be things that I gloss over because it's just too much work for this quick demo.

You can judge which is which.

RE: CQWS - part 2

Today we'll focus on the interaction with ClearQuest. We'll create a module (utilies.pm) that will log into CQ and give us a session object to play with. We'll create another module (query.pm) to use this session object to identify and run queries.

To verify our work, we'll play with a little test perl script.

Find the installation location of your ActiveState Perl. Typically, it is a directory called C:\Perl. Navigate into the site library for your Perl. Typically, this is a directory called C:\Perl\site\lib. Create a directory for our new web service called CQWS. Inside the CQWS directory, create a tests directory.

In my case, I have these directories:

C:\Perl\site\lib\CQWS
C:\Perl\site\lib\CQWS\tests
Create a new file in CQWS called utilities.pm. For now, this package will have exactly one subroutine, designed to log into CQ and return a session object representing our logged in connection to ClearQuest. Some day, we might put some error checking in here and we will probably want to figure out how to keep from being shackled to a single userid in a single CQ database. But that's future fun. Here is my utilities.pm:

package utilities;
use Win32::OLE;

my $DEFAULT_DBSET = '2003.06.00';
my $DEFAULT_DB = 'SAMPL';
my $DEFAULT_USER = 'admin';
my $DEFAULT_PASSWORD = '';

sub getLoggedInSession
{
my $session = Win32::OLE -> new('ClearQuest.Session') or die ("Cannot create session");
$session->UserLogon($DEFAULT_USER, $DEFAULT_PASSWORD, $DEFAULT_DB, 1, $DEFAULT_DBSET);
return $session;
}

return 1;

Now, Perl experts are going to notice that we're missing the initial she-bang. Don't worry about it; we're only doing this for show and, besides, we have to do this on Windows, any way. (Some of you may not like my curly-brace style. All I can say to that is: I don't care.) Also, I'm a little too lazy with case, and, of course, this isn't a nice fully-formed module, but it'll do the job.

Now, if you run this module straight, nothing's going to happen. The action is wrapped inside a subroutine and the only thing that happens outside the action is the setting of variables. The variables are important because they tell us how to log into ClearQuest (including where the heck it is), but they don't do anything. I believe in testing every time I save, if possible, so how do we verify that this will do anything? Let's make a tester.

In the tests directory, create a file called test.pl and edit it. We want to see if we can use the new module to get a session object, then use that session object to see a list of queries. My test.pl looks like this:

require("CQWS/utilities.pm");
my $session = utilities->getLoggedInSession();
my $workSpace = $session->GetWorkSpace();
my $queryList = $workSpace->GetQueryList("1");

foreach ( @$queryList)
{
print $_ ."\n";
}

Run the script from the command line. Here's what I got:

C:\Perl\site\lib\CQWS\tests>perl test.pl
Public Queries/Reports/State Query
Public Queries/Keyword Search
Public Queries/All Defects
Public Queries/Email Rules/All Email Rules
Public Queries/UCMSystemQueries/UCMCustomQuery1
Public Queries/UCMUserQueries/MyCompletedWork
Public Queries/UCMUserQueries/AllActivitiesInStream
Public Queries/UCMUserQueries/ActiveForStream
Public Queries/UCMUserQueries/ActiveForProject
Public Queries/UCMUserQueries/ActiveForUser
Public Queries/UCMUserQueries/MyToDoList
Public Queries/UCMUserQueries/UCMProjects
Now, isn't that exciting? You've created a Perl module that talks to ClearQuest. Other Perl scripts can use this module to get CQ sessions and do things like get query lists. Now, we want to make another module for the sort of query manipulation we're really interested in.

Back in the CQWS directory, edit a file called query.pm. We already know how to get a list of queries, so let's make the module have a subroutine to list out the queries.

package query;
require("CQWS/utilities.pm");
sub get_query_names
{
# --
# INPUT nothing
# RETURN a reference to an array of query names
# --
my $session = utilities->getLoggedInSession();
my $workSpace = $session->GetWorkSpace();
my $queryList = $workSpace->GetQueryList("1");
return $queryList;
}
return 1;
Note that the list of queries is returned from CQ as a reference to an array (not the array itself). To use it, you have to dereference it.

Again, this creates a package of subroutines, but if you run it straight, it doesn't do anything. Let's go back to our test script and rewrite it to use this module. It doesn't have to know about the utilities module directly because the query module does.

require("CQWS/query.pm");
my $queryList = query->get_query_names;

foreach ( @$queryList)
{
print $_ ."\n";
}
In fact, we could stop now and run off to make the producer, but it would only prove that the producer could respond to a request. We also want to prove that the producer can respond to a request and some input data. Our example will be a request to run a particular query, so we'll need to be able to ask for a particular query, run the query and return the data.

What do we want the data to look like? Let's return it as a reference to an array of hashes. Each item in the array will represent one record returned from the query. Each record is presented as a hash where the key is the field name and the value is the contents of the field.

Let's add a subroutine to our query module.

package query;
require("CQWS/utilities.pm");
sub get_query_names
{
# --
# INPUT nothing
# RETURN a reference to an array of query names
# --
my $session = utilities->getLoggedInSession();
my $workSpace = $session->GetWorkSpace();
my $queryList = $workSpace->GetQueryList("1");
return $queryList;
}
sub execute_named_query
{
# --
# INPUT name of a query
# RETURN an array of hashes
# representing the query results
# --
my $result; # the hash we're going to return
shift;
# -- TO DO: Add some error checking
my $queryName = $_[0];
my $session = utilities->getLoggedInSession();
my $workSpace = $session->GetWorkSpace();
my $query = $workSpace->GetQueryDef($queryName);

# --
# structure the return data so that
# @recordSet is an array of records
# ($record an object containing the nth record returned from the query)
# $record->{'column_name'} = $value
# ($value is what's in the column called column_name for this record)
# --
# -- TO DO: Since we'll eventually want to run queries that we've
# -- previously defined AND queries we know the name of, the rest
# -- of this is a prime candidate for making into another subroutine
my @recordSet;
# -- build and execute the query
my $resultSet = $session->BuildResultSet($query);
$resultSet->Execute();
# -- Cycle through the columns and build the result data structure
my $columnCount = $resultSet->GetNumberOfColumns;
my $status = $resultSet->MoveNext;
while ($status == 1)
{
my $record;
my $columnNumber = 1;
while ( $columnNumber <= $columnCount )
{
# -- what is the column name?
my $label = $resultSet->GetColumnLabel($columnNumber);
# -- what is the value in this column?
my $value = $resultSet->GetColumnValue($columnNumber);
$record->{$label} = $value;
$columnNumber = $columnNumber + 1; # move to next col
}
push(@recordSet,$record);
$status = $resultSet->MoveNext;
}
return \@recordSet;
}
return 1;
Pretty simple, eh? Really, it's fairly standard query stuff for ClearQuest. The only real oddity (aside from lack of error trapping) is that we're passing references to arrays instead of the arrays themselves. I'm doing this for pure convenience: I think it's going to be easier to make the XML producer with references. The next step is to modify our test script to test it. My new test script looks like this:

require("CQWS/query.pm");
my $queryList = query->get_query_names;

foreach ( @$queryList)
{
print $_ ."\n";
}
my $name = "Public Queries/All Defects";
print "Testing with " . $name . "\n";
my $queryResult = query->execute_named_query($name);
# -- loop through all the results
foreach ( @$queryResult )
{
my $record = $_;
foreach ( sort keys %$record)
{
print "\tcolumn: $_ \tvalue: " . $record->{$_} . "\n";
}
print "\n";
}
So, I decided to run my All Defects query. I happen to know there are a lot of defects in my SAMPL database, so I should get some result.

Here's what happened:

C:\Perl\site\lib\CQWS\tests>perl test.pl
Public Queries/Reports/State Query
Public Queries/Keyword Search
Public Queries/All Defects
Public Queries/Email Rules/All Email Rules
Public Queries/UCMSystemQueries/UCMCustomQuery1
Public Queries/UCMUserQueries/MyCompletedWork
Public Queries/UCMUserQueries/AllActivitiesInStream
Public Queries/UCMUserQueries/ActiveForStream
Public Queries/UCMUserQueries/ActiveForProject
Public Queries/UCMUserQueries/ActiveForUser
Public Queries/UCMUserQueries/MyToDoList
Public Queries/UCMUserQueries/UCMProjects
Testing with Public Queries/All Defects

column: Contact value:
column: Headline value: A Test Ticket
column: State value: Submitted
column: dbid value: 33555394
column: id value: SAMPL00000962

column: Contact value:
column: Headline value: A Test Ticket
column: State value: Submitted
column: dbid value: 33555382
column: id value: SAMPL00000950
Actually, it went on for a while longer, but I stopped it because I see that it works.

So, we have a couple of Perl modules that talk to ClearQuest. They present a method for running a query we name and give us back a bit of structured data representing the results of the query. Unfortunately, to run this and look at the results, so far all we've been able to do is run a local Perl script against the modules. We haven't yet done anything that works remotely or talks in XML. We don't really yet have a producer. All we really have done is refined our architecture to help with componentization, I guess. Maybe you'd better come back again.

Next time: Creating a Producer.


All the parts in this series:

All Politics Is Local

Did I miss something or is there not actually an election in Prince Georges County this year?

I've mentioned before that there are candidates and flackies out beating a campaign trail in front of the Silver Spring Metro. Most of them seem to have picked up on the idea that people coming off the Metro into Silver Spring in the morning are probably not actually living in Silver Spring, which is good. Every morning there are at least two different camps fighting for votes out there.

I board the train at Greenbelt in the morning. I have yet to see a single candidate over there. Am I just leaving too early or are PG candidates not as interested in the commuter vote as Montgomery candidates? What gives?

ClearQuest SOA: Part I/Set Up

This is the first in a series.

So, if you're not a ClearQuest uber-geek, you're going to want to skip this one.

One of those terms that keeps floating around these days is Service Oriented Architecture, also known as SOA. SOA seems to be the logical next step in true componentization. The idea is that you create a producer. The producer sits out there on the web somewhere and listens for consumers who will come along and ask for stuff. Assuming that the contract between producers and consumers is well-defined and common-protocol based, then consumers and producers can be written in different languages, but still operate as a system.

So, what's the big deal, and what does it have to do with ClearQuest?

Well, one of the things that drives me crazy with ClearQuest is that its API is only easily accessible using Perl or VB Script. Furthermore, even though there's a nice web client, anything fancy you want to do with integration has to be done via Perl or VBScript using an installed client, which has to be updated if there's a release or managed individually, anyway, from a licensing standpoint.

What if you could set up a website that exposed certain methods from the ClearQuest API, but it communicated in XML? Then, you could write any kind of application as a consumer, as long as it could create and read XML. Then, you're operating with SOA.

For example, say you want to have CruiseControl running on Linux. CruiseControl goes out to your source code repository, pulls out the code, and builds it every 20 minutes. Wouldn't it be neat if you could have an Ant task as part of the CruiseControl run that goes in ClearQuest and verifies that a CQ id embedded in your source's comments is valid? How about if it could attach to CQ and change the state of the associated ticket? If you had a ClearQuest producer out there talking XML, then it would be fairly straightforward to create such an Ant task.

Even better, the Ant task wouldn't require that you install ClearQuest or even Perl on your Linux box. All you'd need is to have written a little java class to make a task definition for Ant, and included that class in your classpath.

CQWS - part 1

Suddenly, your interaction with ClearQuest is implementation agnostic!

Of course, the possibilities don't end with Ant. You could be writing anything to talk to the xml, maybe some sort of reporting tool or an importing tool. It's up to you.

A few months ago, a gentleman presented this kind of approach at the Rational Developer's Conference. I didn't get to attend that, but I was given the guy's web address. Unfortunately, the site seems to have given up the ghost. So, I thought I'd give it a try. I was pretty surprised by how easy it was to get a simple proof of concept up and running.

The proof of concept: An Ant task passes a public query name via XML to a service that returns in XML the results of running the query. The Ant task is the consumer, the service running on the server is the producer. The producer will be written in Perl, running inside an Apache web server. The consumer is written in java, executing via Ant.

So, if you'd like to follow along as I work out implementation of a simple proof of concept, you'll need to start with set up:

(When I say producer and consumer, these can be the same machine. The important bit is that the consumer doesn't communicate directly with ClearQuest or the CQ database, but through the service, which provides a layer of abstraction.)

Producer

The producer uses Perl and a ClearQuest client to talk to the ClearQuest database. It is served up by Apache CGI to talk to consumers via RPC.

  • Install a ClearQuest Client
  • Install ActiveState Perl 5.6 (It's free!)
  • Install the Frontier Module using ActiveState's PPM (on the command line: ppm install Frontier-RPC)
  • Install Apache Web Server (or the Rational Web Platform)

Consumer Our consumer will use Java/Ant to talk to the producer via XML over HTTP. We will not have to install ClearQuest on the consumer.

You're going to need some extra jars, but we'll get to that when we start discussing the client. At the rate I write, that might be months down the road.
All the parts in this series:

The Alchemist (Paul Coelho)

Mother and Child Statue Fall
So, I'm hiding behind the Mother and Child statue down at Roosevelt Center. Bob is "hiding", too, but he's not very good at it. I, of course, consider myself an expert hider. One problem with Bob's technique is that it involves an endless stream of babble. He won't shut up.

"He's standing by the theatre looking at the movie poster," Bob informs me. He's talking about The Admiral, who floated into the Center a few minutes ago. The Admiral doesn't walk; he sails. Not like some small Biloxi Dinghy, but more like The Love Boat, smoothly and peacefully.

You might remember Bob -- he's Bertie's skittish brother. You wouldn't call Bob a man of confidence. Shadows worry him. Change scares him. Confrontation drives him to apoplexy.

"I'm not sure when they're going to switch movies," Bob goes on. "Do you think it'll be this week or next week?"

I cannot decide whether knowing or not knowing will bother him more. I hazard a guess that the movie will change this week.

"Oh, dear," he says. "What if I don't get to see it in time?"

"Did you want to see that movie?"

"I don't know. Maybe? Aaaagh!" The second problem with Bob's technique is that he's twitchy. He jumps and squawks at every change in the wind. This is no way to maintain inconspicuosity.

"What's wrong?" I ask, as conversationally as possible.

"He's heading for the New Deal Cafe."

"Ah."

"And he's dancing."

"What?"

"He's dancing," Bob repeats. He demonstrates, bobbing his head back and forth and twitching his fingers. He shrugs his shoulders and grimaces to a silent beat, but everything below his stomach remains unmoving. It's an impressive imitation.

"Stop that," I say. The third problem with Bob's hiding technique is that he forgets that he is hiding. He has drifted away from Mother and Child in the direction of the doctor's office. Personally, I'm trying to edge toward the grocery store to keep the concrete family between me and The Admiral.

"Oh, right," he says as he skitters back. "Why are we hiding, again?"

I sigh. "I don't know why you're hiding. I'm hiding so I don't have to talk to The Admiral about that book. Ever since he read it, he's turned into an evangelist."

"He became a Christian?"

"No. I mean that he constantly wants to talk about it and convince you that it's a good book to read. Worse, he thinks you can be improved by reading it."

"What's wrong with the book?"

"What's not wrong with it? How could somebody write a book that tells people they won't learn anything from books? It's a traitorous act akin to a medical doctor telling people to smoke, or a teacher telling kids that school is a waste of time."

"Or a movie critic telling people not to go see a movie?"

"Yes. No. Not at all. A critic has to tell people the truth, and sometimes a movie isn't worth seeing. Now, if the critic said that movies weren't important and that nobody should ever watch a movie again, that would be the same."

"So, he's not like a book critic?"

"No, he's an author who suggests that books are for misguided losers. He's insulting every person who reads his book, nay, each and every person who has ever flicked through the pages of a treasured tome. Furthermore, his overall theme is terrible for society as a whole. I dare say that if everyone started living the way this book suggests, the world would be full of non-productive people looking for their personal treasures, living in the moment and singing songs from Rent. Is that --"

"Gentlemen!" The Admiral says from behind me. I very nearly jump out of my skin. Bob keels over sideways. (Don't worry. He recovers quickly.) "What deep and interesting things are you discussing?"

"Not much," I say.

"Listen," he says, "I've been reading this book and --"

"No," I interrupt. "We don't want to hear about The Alchemist."

"Oh, I haven't thought about that book for a while."

"I mean," I continue, "there's that whole thing with the teaching. The sheep are his teachers. The sand is his teacher. Those palm trees and the camels and the shards of glass from the shattered hopes and dreams of an elderly shopkeeper -- they're all his teacher."

"Ah, well --" admits The Admiral.

"Let me tell ya, buck-o." I find myself standing on the bench and waving my hands in the air. "Camels do not teach. Sheep do not teach. Sheep eat. They're great eaters. They really know a thing or two about eating. They're definitely food processing experts. You might learn something from a sheep or two, but they will never teach you anything, not even about eating, because sheep cannot communicate. Do you know how I know they don't know how to communicate?"

"Uh."

"Because they don't write books," I say triumphantly.

"Oh, look," says The Admiral. "There's iLyAiMy. Gotta go."

The Admiral motors off. I climb back down from the bench and sit with my head bowed. I'm a bit out of breath and my shoulders are sore.

"I think there's a problem with your hiding technique," Bob tells me. I know that. I have a tendency toward pontification.

1:40/1:30

The weather was great for a ride yesterday. There was a deer on the Sligo Creek trail. Still, I was much slower than expected. This is mostly due to trying alternate routes. Gosh, I hate hills.

Oh, and the light at Metzerrot and Route 1 still doesn't respond to pushing the dang button.

Trail Conditions

The Sligo Creek trail between Piney Branch and New Hampshire has had some work done to fix problems resulting from this summer's flooding. A full section north of the hospital (Carroll Ave) was repaved and there is new gravel and patched pavement in other places. It seems like the most work was between Park Valley and Carroll. Sadly, the parkway is open again, so we're back on the trail, I guess. There are a few places where the trees and bushes are starting to crowd the trail. Who maintains that?

The North West Branch (NWB) trail is a horrible mess. I don't normally pick it up south of the neighborhood just above East-West Highway, so I can't say what the trail used to be like, but I think the trail is unusable between Ager and East-West. There are terribly large chunks of pavement missing, and what's left has in places been warped into six or eight inch folds. It really does look like cloth folded up, and it's not rideable. Also, the bit of Sligo Creek trail just before the juncture with the NWB trail and the NWB trail have fallen trees blocking parts of the thoroughfare. My county should be ashamed.

Route Change Tests

My typical route is now 11.2 miles. I like it because it avoids several hills, but it's longer than I would like. There is still a rather large hill at Park Valley when I'm coming into Silver Spring, but it cuts off a nice chunk.

Using this route home is a little painful trying to get from Sligo Creek Trail to the NWB trail by riding through the neighborhoods around Riggs Road. So, I tried to use the trail system all the way to the junction. As I said earlier, the NWB trail is a mess down that way, and it took me some extra time to go this way. Here's a map of that try. Looks like I added a mile or so.

Another part of the regular trip that I'm not so happy with is the trip down Greenbelt Road. I don't mind the traffic so much, but it's a bit tight near the mall and the bridge over Kenilworth is always covered with debris. (That's where I picked up the drill bit in my tire a little while ago.) So, I tried to go over the top (by walking through the Metro/Marc station. This wasn't too bad, actually, but then I tried to avoid the Park Valley hill by going further north. I got all confused and went way out of my way. Worse, I picked up a harder hill. Overall, I added another couple of miles or so. Here's a map of this mistake.

Rough

A Bit of a Whinge

Wow, it's been a rough couple of weeks, and I can't quite put my finger on why. There's a lot going on at work, but we don't have a release for another month. The heat has been unbearable and I've been foggy from no biking. The Brunette has been sitting a dog that requires a lot of attention and time, so our schedule's been odd. I'm just generally tired and looking forward to winter.

I got a new laptop. It's much faster than the last, but it's taking forever to transfer stuff over. Every few hours, I remember that I need this program or that set of files. The customer blocks some sites, so I can't download iTunes or get my bookmarks straight. Oi, life is so dang hard!

And to top it off, tomorrow's Into the Woods is sold out!

Digital People (Sidney Perkowitz)

"Am I a bad person?" I ask from the bench outside the Greenbelt Theatre. I like to sit under the trees and bathe in the aroma of freshly popped corn.

"Why do you ask?" replies Prasad, careful as always. He and his slightly larger friend -- I can never remember the lad's name, so let's just call him Jimmy for this story -- are busy with chalk on the sidewalk. The chalk marks are bright and cheerful.

"What are you drawing so diligently on the plaza?" I ask. I hadn't meant for my earlier query to pop out, so I'm trying to change the subject. Passersby ignore me -- Greenbelters are used to people who talk to themselves. Prasad and Jimmy attract no attention simply because they don't really exist. Prasad's tie is covered in chalk dust. I'm not completely sure whether he heard me or not.

"Have you read that book called Digital People, all about how close we are to having androids and stuff?" Prasad asks this without looking up from their drawing. I think I can see formulae and timelines mixed in with the diagrammatic elements. I lean forward to touch the chalk marks. They feel, well, chalky.

"Don't you think most people who had seen Star Trek would refer to Data as 'he'?" I reply. Prasad nods. In one section of the book, the author spends some time discussing Commander Data. Data, an android, was granted personhood by a tribunal, but the author refers throughout to Data as "it". I found it rather distasteful.

"Do you realize you haven't explained your earlier query regarding your level of evilness?" Prasad asks. I don't think I said anything about evil, did I?

"And are you aware that you never answered my question about your diagrammatic scheming here?"

He nods thoughtfully and crosses a "t".

"Do you think that if humanity had a common enemy, an enemy of indescribable power and logical might, that maybe we'd all unite against the common threat and stop fighting one another?"

"Are you saying that digital people could be that threat?" I ask. Now I recognize nor and nand gates in the chalk-drawn diagrams. There's also a face with exed out eyes and protruding tongue.

"Why not?" Prasad asks. Jimmy throws down his piece of chalk and jumps to his feet.

"Will you two stop it?" he shouts. "Do you know how annoying it is to listen to your constant question game?" He stomps around one of the bush squares, grumpy and inarticulate. Prasad tries valiantly to keep him from walking on their plans. "Why did my mom move to this crazy town full of crazies, any way?"

I look over at the dry cleaners. I wonder briefly how much business they really do. But Prasad is not so shy.

"Do you realize that you're talking in questions, too?" he asks a little bit triumphantly. Jimmy jerks to a halt and sits down in the square. He sighs. Prasad and I watch people going in and out of the grocery store for a few minutes. My thoughts return to the book.

"Do you remember that stuff in the book about lampreys and monkeys?" I ask, eventually. Scientists hooked up lampreys to drive little cars using nerve signals only -- the lamprey brains were removed and placed in containers in cars. Other scientists operated on monkeys, eventually getting them to move a computer cursor using their only brain waves.

"And were you upset and appalled by the treatment of the lampreys?" Prasad wanted to know. I nodded, and he continued, "Did you then think to yourself, 'but cyborgian video-game-playing monkeys...Cool!'?"

I nodded, somewhat abashed.

"Yes," Prasad pronounces after a minute of reflection. "You're pretty evil all right."