Today's Web applications often have to reach a world-wide audience on a wide array of browsers and pervasive devices. This article describes how you can leverage transcoding and machine translation to extend the reach of your Web applications, without re-authoring your content or applications.
For example, let's say that you have a corporate intranet Web site that includes an employee phone book and product pricing information. All of the current content is authored in HTML and English. Now, let's assume that your sales personnel in Germany need access to this information from their mobile phones. By using IBM ®; WebSphere®; Transcoding Publisher (hereafter called Transcoding Publisher) and IBM WebSphere Translation Server (hereafter called Transcoding Publisher) you can extend the existing content to these users. You can do this without the expense of reauthoring the content in WML and having it professionally translated.
Transcoding Publisher lets you dynamically convert HTML pages into almost any markup language. By adding transcoding to your Web applications, you can extend them to devices which require different markup languages. With very little effort and no additional artifacts to maintain, you can enable applications which were originally designed for wired-network PCs to reach wireless device users.
Translation Server lets you automatically convert text from one language to another, within a collection of supported national languages. Using Translation Server, you can enable users to quickly and easily get a "gist" translation of your content. Therefore, instead of hiring professional translators, you can use machine translation to enable communication by providing an approximate, understandable translation.
You can use Transcoding Publisher and Translation Server together to extend your application to users of wireless devices in other countries. Transcoding Publisher transforms Web data from one format to another while Translation Server transforms content from one national language to another. An application that uses both would flow something like this:
- A German user requests a Web application that is written in English by directing the wireless browser on their Web-enabled cell phone to the English Web site.
- Transcoding Publisher sends a stream of HTML data to Translation Server so that it can be translated. For example: HTML containing English content is sent to Translation Server.
- Translation Server returns the HTML intact, but the content is in another national language.
- Transcoding Publisher then converts the HTML into the markup language required for the user's mobile device.
- The German user sees the Web application on his or her device, with the English text translated into German.
Understanding the Machine Translation Transcoder
Transcoding Publisher's functions are encapsulated into units called transcoders. Each Transcoding Publisher transaction goes through a series of transcoders depending on the state of the content requested and the capabilities of the client device. The steps to call the facilities of the Translation Server are built into a single transcoder, called the Machine Translation Transcoder (MTT).
The functions of Translation Server can be used in many ways. The translation server is a layered Application Programming Interface (API) on top of different language translation engines. One way to use the translation server is to code to the APIs to call the Translation Server translation engines directly from your applications. This is an easy approach for rapid deployment of smaller applications.
For larger applications with more demanding translation requirements, Translation Server provides an API that uses Remote Method Invocation (RMI) to send translation requests to another server. You can dedicate the processor of the Web application server to processing Web applications and the processor of the translation server to performing translation. By spreading the processing load among servers with specific tasks assigned, the users of your Web site will have a much more responsive Web browsing experience.
The MTT uses RMI to divide the work; therefore, you can choose to use separate servers to share workload. However, the overhead of the RMI connection is negligible so there is no performance degradation if you decide to use both components on the same server.
Transcoding Publisher can transcode between different markup languages much more quickly than Translation Server can translate between national languages. The MTT lets you specify as many translation servers as you need for your environment. You purchase Translation Server licenses by language pairs; for example, English -> Spanish and English -> German are separate licenses. If you can dedicate a server to translating a single language pair, you can optimize performance.
The Translation Server can currently translate between the following pairs of national languages:
- English-to-French, French-to-English
- English-to-Italian, Italian-to-English
- English-to-German, German-to-English
- English-to-Spanish, Spanish-to-English
- English-to-Chinese (simplified)
- English-to-Chinese (traditional)
- English-to-Japanese
- English-to-Korean
Figure 1 illustrates the relationships among Transcoding Publisher, Translation Server, and MTT and shows how a typical request for translation flows.
Figure 1. Translation request flow
-
Client makes HTTP request to Transcoding Publisher
-
Transcoding Publisher proxies the request and forwards it to the content server
-
The content server responds to the request with an HTML document
-
Within Transcoding Publisher the MTT runs and decides that this document needs to be translated
-
The MTT passes the HTML document to the Translation Server
-
Translation Server translates and returns the document
-
Transcoding Publisher transcodes the HTML document into the appropriate markup language for the device.
Applications that access Translation Server and the MTT, need to determine these three pieces of information:
- The original language of the document to be translated.
The MTT refers to this language as thecontentLanguage. - The languages that the client's browser can support.
MTT refers to the target language as theacceptLanguage. - Optionally, the content topic of the the document to be translated.
MTT refers to the content topic as the
subject.
Ideally, the client would send the
Accept-Language HTTP header field with its
request and the application server or servlet would send a
Content-Language with its response. However,
Web pages and their hosting Web sites often do not explicitly specify the
content language.
Each of these values can be obtained from a variety of places. If you own the content that needs to be translated, then you can probably also determine the source and target languages. If your Web application is written in English, you would set the default language to be "en".
However, if you don't own the content and the values are not available, the MTT uses a set of mechanisms, discussed later in this article, to determine the values.
Transcoding Publisher is built on an open framework called Web Intermediaries (WBI) which allows additional plug-in programs to be written and added quite easily. If you are working with content served by your own Web servers, it is simple to write intelligent agents that can guess the language of a requestor or a response page. These plug-ins can attach bits of information to the Transcoding Publisher transaction as it passes from one plug-in to the next. As long as your plug-in runs at a higher priority than the MTT, then the MTT will take advantage of the additional information and translate accordingly. You will see how to set extra rule keys later in this article.
Because Transcoding Publisher can identify the device, network, and user of the requestor, you may want to set default values in the device, network, and user profiles. These values are added to the Transcoding Publisher Preference Aggregator for the transaction and can be used by the MTT. For example, if you feel confident that any user contacting your site using an i-Mode phone wants a Japanese translation, you can add that information to the i-Mode phone's device profile.
If any of these values cannot be determined, the MTT will use its defaults. These are defined for the transcoder and you can export them from Transcoding Publisher and edit them using any text editor.
Configuring the Machine Translation Transcoder
You've seen that the MTT has a handful of parameters that can be set and that there are a variety of ways to set them. Now, let's look at how to configure the transcoder.
First, to use the MTT you need to enable it in Transcoding Publisher and specify the translation servers you will use.
- From the WebSpher Transcoding Publisher 4.0 Administration Console, select Machine Translation Transcoder.
- Click Enabled.
- Refresh the server.
After you register the MTT, you need to define the translation servers that it will use. You can do this in three ways.
- Editing the properties file for the transcoder (file-system install only)
- Using the DMT LDAP configuration tool to edit your LDAP config-set
- Exporting the Transcoding Publisher configuration, editing it and importing it back into Transcoding Publisher
All of the translation transcoder properties can be edited in these same three ways.
Most of the Transcoding Publisher settings are in small text files on the
server's file system. Transcoder properties are in a *.prop file that is
packaged in the transcoder jar archive. For example,
MachineTranslationTranscoder.prop is included in
MachineTranslationTranscoder.jar. When a transcoder is registered, the
*.prop file is copied into the Transcoding Publisher
etc\plugins directory.
The *.prop file contains text key/value pairs similar to Java's
java.util.Properties class. The MTT
configuration properties in
etc\plugins\ibm\MachineTranslationTranscoder.prop
are shown in Listing 1.
Listing 1. Sample MachineTranslationTranscoder.prop
#Properties of Machine Translation Transcoder Class=com.ibm.transform.textengine.MachineTranslationTranscoder Major=1 Minor=0 Name=Machine Translation Transcoder Condition=(content-type=text/html) Priority=45 servers=localhost:1098 DefaultContentLanguage=en DefaultAcceptLanguage=en DefaultSubject= |
Editing LDAP installation settings
The configuration properties for MTT are the same values used regardless of the backend storage that is used. Therefore, for a file system installation of Transcoding Publisher, you can edit these values directly in the prop file. For an LDAP installation, you have to edit these values with an LDAP configuration tool such as DMT.
-
Class=The Java class that contains the transcoder plug-in -
Major=Major release number -
Minor=Minor release number -
Name=Name of the transcoder -
Condition=Condition under which this transcoder will run. For example, if you set Condition=(content-type=text/html), the transcoder will run only for HTML content -
Priority=Priority at which this transcoder will run (the higher the priority, the earlier the transcoder will run) -
servers=Comma-separated list of servers -
DefaultContentLanguage=Default Content-Language value. This is used if the content-language cannot be determined from the other sources as explained above. -
DefaultAcceptLanguage=Default Accept-Language value. This is used if the accept-language cannot be determined from the other sources as explained above. -
DefaultSubject=Default value for the translation subject. This refers to the idea of a subject as supported by the Translation Server. If the Translation Server translation knows the subject of the document it is translation, it can make more informed decisions on the meanings of words that have multiple definitions.
Setting properties on multiple servers
You can export and import configuration settings in Transcoding Publisher servers. The settings are stored in XML files; therefore, you can edit them with a text editor. Using the XML import function, you can configure many servers without using the WebSphere Transcoding Publisher Administration Console GUI on each server. In this example, you see how to specify multiple translation servers by exporting a configuration, editing it, and importing the configuration into each Transcoding Publisher server you want to configure.
To export the existing configuration, in the Administration Console:
- Select Transcoders -> ibm ->Machine Translation Transcoder.
- Click File -> Export.
- Specify the name of the file under which you want to save the
configuration. In this example, we'll use example
MachineTranslation.xml.
Figure 2. Exporting Transcoder settings
The configuration file is exported to your file system, using the name you specified. The file is shown in Listing 2.
Listing 2. Exported XML Machine Translation Transcode Configuration file
<?xml version="1.0" encoding="UTF-8"?>
<!--Date and Time of export :Mon Jan 7 16:59:45 EST 2002-->
<!--Description : Exported - Machine Translation Transcoder-->
<Resources ServerModel="configSet1" Version="magic40">
<Plugin>
<Folder>ibm</Folder>
<SelectorName>MachineTranslationTranscoder</SelectorName>
<Description>Translates document information from one language to
another using WebSphere Translation Server</Description>
<Name>Machine Translation Transcoder</Name>
<Title>Machine Translation Transcoder</Title>
<Class>
com.ibm.transform.textengine.MachineTranslationTranscoder
</Class>
<Version>
<Major>1</Major>
<Minor>0</Minor>
</Version>
<Priority>45</Priority>
<Condition>(content-type=text/html)</Condition>
<ClasspathDependencies>
file:/E:/WTP40-GM/addedPluginJars/ibm_MachineTranslationTranscoder.jar
</ClasspathDependencies>
<Keys>
<Key Name="DefaultAcceptLanguage">en</Key>
<Key Name="DefaultContentLanguage">en</Key>
<Key Name="servers">localhost:1098</Key>
</Keys>
<Enable>true</Enable>
</Plugin>
</Resources>
|
Now that you have the file, you can quickly and easily add additional translation servers to the list. By default, RMI will try to connect on port 1099; therefore, all you have to specify are the IP addresses of the servers.
Listing 3. Modified Machine Translation Configuration file
<?xml version="1.0" encoding="UTF-8"?>
<!--Date and Time of export :Mon Jan 7 16:59:45 EST 2002-->
<!--Description : Exported - Machine Translation Transcoder-->
<Resources ServerModel="configSet1" Version="magic40">
<Plugin>
<Folder>ibm</Folder>
<SelectorName>MachineTranslationTranscoder</SelectorName>
<Description>Translates document information from one language to
another using WebSphere Translation Server</Description>
<Name>Machine Translation Transcoder</Name>
<Title>Machine Translation Transcoder</Title>
<Class>
com.ibm.transform.textengine.MachineTranslationTranscoder
</Class>
<Version>
<Major>1</Major>
<Minor>0</Minor>
</Version>
<Priority>45</Priority>
<Condition>(content-type=text/html)</Condition>
<ClasspathDependencies>
file:/E:/WTP40-GM/addedPluginJars/ibm_MachineTranslationTranscoder.jar
</ClasspathDependencies>
<Keys>
<Key Name="DefaultAcceptLanguage">en</Key>
<Key Name="DefaultContentLanguage">en</Key>
<Key Name="servers">
192.168.1.2, 192.168.1.3, 192.168.1.4, 192.168.1.5
</Key>
</Keys>
<Enable>true</Enable>
</Plugin>
</Resources>
|
You can register as many servers as you have, and you don't need to tell Transcoding Publisher which languages each server supports. When the Machine Translation Transcoder initializes, it polls each specified Translation Server instance to find out which languages it knows. Using this information, the MTT uses a load-distribution algorithm to send each translation request to the least-recently-used server which has the required translation engine.
There are times when the defaults and the information in the HTTP header fields just aren't sufficient. For example, many Web servers do not identify the content-language of the pages that they are serving. In cases such as these, the Machine Translation Transcoder provides administrators another mechanism to set values that are used for translation called extra rule keys .
The WBI framework lets you set extra rules in one Transcoding Publisher
plug-in that can be used by a subsequent plug-in. The Machine Translation
Transcoder looks for extra rules for
$contentlanguage and
$wtssubject . You can set these values using
other plug-ins or annotation.
If you are familiar with Transcoding Publisher plug-ins, you can add an extra rule key with just a couple lines of code. The code in Listing 4 shows how to add an extra rule key to a transaction. This code adds the extra rule key "$contentlanguage=es" to each transcation.
Listing 4. Setting an extra rule key
public void handleRequest(RequestEvent requestEvent)throws
RequestRejectedException {
DocumentInfo info = (DocumentInfo) requestEvent.getRequestInfo();
HttpRequestHeader header = info.getHttpRequestHeader();
info.setExtraRuleKey("$contentlanguage","es");
}
|
Listings 5 and 6 show how a plug-in can set an extra rule key using values in a properties file. The idea here is that you can build a table of Web sites that your users will need translated. By using this table, you can correlate a different language with each Web site. This could be used for an enterprise with locations in different countries. In a case like this you have a finite number of domains each referring to a different country or a different national language.
The plug-in checks the URL of the requested page against all the keys in
the properties file. If any of the keys are contained in the URL, the
response is tagged with a $contentLanguage
value set to the value from the properties file. Since IBM has many
different labs around the world, this plug-in identifies the language used
in the intranet of each site.
Listing 5. Sample Request Editor properties file
Class=MTRequestEditor pluginName=AddVariablesRequestEditor Major=1 Minor=0 ch.ibm.com=de de.ibm.com=de jp.ibm.com=jp lagaude.ibm.com=fr |
Listing 6. Plug-in that sets extra rule keys
import java.util.*;
import java.io.*;
import com.ibm.wbi.*;
import com.ibm.wbi.persistent.*;
import com.ibm.wbi.protocol.http.*;
import com.ibm.wbi.util.*;
public class MTRequestEditor extends HttpPlugin {
/**
* Called when the Plugin is initialized.
* This method instantiates the RequestEditor and adds it to the system
*/
public void initialize(){
SystemContext context = getSystemContext();
AddVariablesRequestEditor avre =
new AddVariablesRequestEditor(context);
avre.setup("AddVariablesRequestEditor",
"%true%",97);
addMeg(avre);
}
class AddVariablesRequestEditor extends HttpRequestEditor {
private static final String SETUP_PROPERTIES =
"/plugins/MTRequestEditor";
String domain = null;
String sourceLanguage = null;
String subject = null;
Hashtable ht = null;
/**
* Constructor method for the RequestEditor
* This method reads the configuration data from the properties file
* and initializes the Request Editor
*/
AddVariablesRequestEditor(SystemContext context) {
Section root = context.getRootSection();
Section addVariablesConfigSection =
root.getSection("/plugins/MTRequestEditor");
// Read the prop file, store the domains/language pairs
ht = new Hashtable();
Enumeration e = addVariablesConfigSection.keys();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
// This RequestEditor uses one properties file for both it's
// registration data
// and the settings that it uses to operate.
// This code ignores the registration data and only remembers
// the settings it will use for setting languages
if ( !(key.equals("Class")
|| key.equals("pluginName")
|| key.equals("Major")
|| key.equals("Minor")
)
)
{
ht.put(key, addVariablesConfigSection.getValue(key));
// System.out.println(key + ": " +
addVariablesConfigSection.getValue(key) );
}
}
}
public void handleRequest(RequestEvent requestEvent)throws
RequestRejectedException {
// System.out.println("AddVariablesRequestEditor::handleRequest");
DocumentInfo info = (DocumentInfo) requestEvent.getRequestInfo();
HttpRequestHeader header = info.getHttpRequestHeader();
// get the URL of the request
String url = header.getUrl();
Enumeration e = ht.keys();
if (e != null) {
while (e.hasMoreElements()) {
String element = (String) e.nextElement();
//System.out.println("Checking: "+element);
// If any of the strings for "keys" matches a
// substring of our URL, set the value for the key
// as the $contentLang
//
// This could easily be extended for setting subjects based
// on all or parts of the URL
if (url.indexOf(element) != -1) {
info.setExtraRuleKey("$contentlanguage",
(String) ht.get(element));
System.out.println("$contentlanguage: "+
ht.get(element));
}
}
}
try {
requestEvent.getMegOutputStream().write(
requestEvent.getMegInputStream());
} catch (Exception ex) {
throw new RequestRejectedException();
}
}
}
}
|
You can download the working plug-in and source code below. MTRequestEditorSample.zip
But, suppose you don't like the idea of writing code and compiling. Can we make it any easier?
Yes! The annotation language in WebSphere Transcoding Publisher 4.0 provides support for adding values that the MTT can use. Administrators can use external or internal annotation to provide subject and content language information about a particular document or group of documents. Administrators can quickly and easily identify the language of a particular Web site. Often, this is much easier than altering your Web applications.
A non-programming alternative: Annotation
Annotation gives you the ability to set the
$contentlanguage and the
$wtssubject values, without programming. You
can create one external annotation file and apply it to a wide variety of
Web sites by using wildcards in the URL value when you register the
annotation.
Let's look at an example. Suppose you want to use Transcoding Publisher and Translation Server to show Yahoo Germany http://www.de.yahoo/ on a RIM Pager, in English. The solution is to use Transcoding Publisher as a reverse proxy server with the MTT plug-in.
Since many mobile devices do not allow you to specify a forward proxy server, Transcoding Publisher's reverse proxy mode is a popular deployment option. A reverse proxy server interacts with a Web client as if it were a traditional Web content server. In reality, a reverse proxy server forwards all the requests that it receives to another server.
When Transcoding Publisher is configured as a reverse proxy server, it can perform transcoding on the output of any Web server without any modifications. In this scenario we want to access yahoo.de with a WML browser running on a 2-way pager. To use this pager, we have to use Transcoding Publisher as a reverse proxy server.
The server at http://www.de.yahoo/ does not identify the content language of the documents it sends out. In order for the machine translation transcoder to know that the document is in German, we create a small annotation file.
Using annotation is simple and fast. The annotation file shown in Listing 7 will associate all content served with the language German. When you register this annotation file with the Transcoding Publisher server, you specify the condition below. This annotation will only be applied when the requested URL contains the string "de.yahoo".
url~*de.yahoo* |
The annotation in Listing 7 sets the extra rule key,
$contentlanguage=de , in the Transcoding
Publisher transaction data. Once this transaction reaches the MTT, the
presence of the extra rule key tells the transcoder that the content is in
German and the MTT will process it accordingly. Many times this is much
easier than altering your Web application to identify its own content
language, especially when you are not the content owner.
Listing 7. Annotation file for yahoo.de
<?xml version='1.0' ?> <annot version="1.0"> <description take-effect="before" target="/*[1]"> <contentlanguage>de</ contentlanguage> </description> </annot> |
Tip: Where appropriate, you can also specify the subject of a document or set of documents in an annotation file. In the case of yahoo.de, it is not appropriate because you cannot assign a single subject to the entire site. In cases where you can, the annotation could would look similar to this:
<wtssubject>computers</wtssubject> |
For multiple subjects, it would look similar to this:
<wtssubject> golf, sports </wtssubject> |
Using translation, we have transformed the German Web site shown in Figure 3, to the English translation shown in Figure 4.
Figure 3. de.yahoo.com
Figure 4. de.yahoo.com translated to English
Finally, Figure 5 shows the result of the translated page which has been transcoding for a particular device.
Figure 5. de.yahoo.com in English on a 2-way pager
The Transcoding Publisher transcoders require different input to do their jobs. Some transcoders (such as the annotation transcoder and most of the HTML transcoders) require the generation of a Document Object Model (DOM) representation of the document being transcoded.
The default priority of the MTT is set to ensure that it runs after the annotation transcoder (if the annotation transcoder is enabled). This prevents WebSphere Translation Server from translating portions of a document that could be removed by annotation.
However, this method has performance trade-offs. If the HTML DOM generator is enabled, the DOM is generated for the annotation step, serialized for translation, and then recreated so that the next transcoder can use it. If annotation is only removing a small amount of text from most Web pages that are being transcoded, a performance gain can be achieved by raising the priority of the MTT so that it runs before the HTML DOM generator.
The priority can be adjusted using any of the methods described earlier for altering configuration values. The priority should be set higher than the priority of the External DOM generator. You can determine its priority by running the Request Viewer tool and viewing the settings of the External HTML DOM Generator. If the priority of the External HTML DOM Generator is set to 92, you need to set the priority of the MTT a higher value, such as, 95.
RMI or Remote Method Invocation is a Java mechanism for calling methods in classes that are instantiated in another Java VM. For most applications, this inter-process communication occurs on port 1099. When more than one application tries to run an RMI registry on port 1099 conflicts arise. Both Transcoding Publisher and Translation Server use RMI and both try to use port 1099.
When you run a Transcoding Publisher instance and a Translation Server instance on the same machine, either Transcoding Publisher or Translation Server needs to be run on a different port. It doesn't matter which server uses which port. Listings 1 and 2 illustrate the use of Translation Server on port 1098.
To reconfigure the Translation Server to use port 1098, open the Translation Server GUI or the properties file and change the value for the port to 1098. Figure 6 shows how to set the port using the GUI.
Figure 6. Reconfiguring the Translation Server
Finally, the entire process of translation via RMI is dependent upon the
presence of a file wts.jar . This library
contains the RMI stubs that are required to access the Translation Server
instances. Transcoding Publisher comes with a copy that matches the first
release of Translation Server. If you have a more recent revision you
won't be able to connect to the Translation Server. You'll see errors such
as this:
java.rmi.UnmarshalException: error unmarshalling return; nested exception is: java.io.InvalidClassException: com.ibm.lt.LTrmi_Stub; Local class not compatible: stream classdesc <b class="rboldcode">serialVersionUID=-6552217074764492800 local class serialVersionUID=-5841007100252799030</b> |
You can find the wts.jar in the
lib directory of your Transcoding Publisher
installation. Simply replace that file with the newer version that came
with your translation server.
Web Intermediaries (WBI, pronounced "webby") is an architecture and framework for creating intermediary applications on the Web. Intermediaries are computational entities that can be positioned anywhere along an information stream and are programmed to tailor, customize, personalize, or otherwise enhance data as they flow along the stream.
The WBI Development Kit is available for download. You can use the toolkit for building many types of Web intermediary applications within the WBI framework, using Java APIs.
One key intermediary application is the transformation of information from one form to another; that is, the process we call transcoding. The WBI Development Kit provides the same plug-in APIs as those provided by Transcoding Publisher. Applications developed with WBI version 4.5 can be used with the Transcoding Publisher product (with a few exceptions), because WBI provides the backbone on which transcoding operations run.
Transcoding Publisher uses preference profiles to maintain a system of preferences that govern how pages are transcoded for a particular device. When a request comes in, Transcoding Publisher identifies the requesting device based on the HTTP header that it sends and matches the device with a device profile. The device profile contains information about the constraints and limitations of the device. This information is passed to each transcoder.
If you are writing custom transcoders, you can add values to the device
profiles that are meaningful to your transcoder. The sample used in this
article is acceptLanguage. After Transcoding
Publisher has identified the device, all of the device profile
information, including acceptLanguage, which
is the language supported by the browser, is sent to each transcoder. In
this way, you can easily specify different values for different devices.
Some pervasive devices do not allow the Accept-Language header field to be manipulated. In this case, the same information can be set in a device or network profile. An administrator sets a specific language for all requests coming from a certain device or coming in on a specific network port.
These additional profile values that can be set for transcoders are called
advanced correllators. You set advanced correllators in the
Administration Console, by clicking the Advanced button for the preference
profile, and adding the key/value pair. For example,
Accept-Language = es.
-
IBM WebSphere Transcoding Publisher
-
WebSphere Transcoding Publisher Developer's Guide
-
Document Clipping with Annotation

Alan Booth is a software engineer for IBM in Research Triangle Park, North Carolina. Alan is currently a software developer and portal technology evangelist in the IBM Pervasive Computing Division. Alan is a graduate of North Carolina State University. Alan is an avid cyclist and likes to consider himself an amateur daredevil. You can reach Alan at aebooth@us.ibm.com . Alan has also written several other developerWorks papers.




