Pick Two

  • Fast, Cheap, Good
  • Schedule, Scope, Resources

Once upon a time, I wondered whether there was a natural law dictating three choices. Now, I understand that three is the minimum number to graphically demonstrate the need for compromise. Two makes it look like you’re pitting one against the other and four makes the choices too complex.

The bottom line is that any commercially desirable goal requires compromise. The sooner you accept the need for compromise, the sooner you can make some decisions and get on with it.

Dig This

Note to self: reserve time at Dig This next time you’re in Las Vegas.

The 10-employee park has five pieces of machinery, including a pair of Caterpillar D5 track-type bulldozers and three Caterpillar 315CL hydraulic excavators. Dig This sells three-hour packages that consist of a 30-minute safety and operation orientation followed by two hours of maneuvering either a bulldozer or excavator.

Donate via the IRS

There are numerous deserving charities that need our support. Especially now. But make a donation and you can expect an unending stream of solicitations for more donations as your reward.

So wouldn’t it be nice if we could make anonymous donations via the IRS. That way we could get a tax receipt without revealing our identity.

Farewell Faithful Steed

After 16 years of service, it is time to say good bye to my 1995 Infiniti G20.

I had been debating between the Mazda 626, Acura Integra, and Infiniti G20 for a while. And I made my move when when the Hendrick Auto Group ran an ad for a white 5-speed G20 in the Sunday Chronicle.

My G20 carried me on a painful commute from the San Ramon Valley to Silicon Valley for several years until I couldn’t endure it any more. It waited patiently in my CA garage for the year or two that I spent in Manhattan. And it rejoined me in Connecticut when I shifted stakes from CA to CT for good.

Sixteen years is a good run for a car. And it still runs great. But my wife upgraded her car and I’m upgrading to her 2005 Subaru. At least I’ll still get to see the G20 when the new owner (a colleague at work) brings it to the office.

Using XSL Param

XSL is not just a transformational process, you can also add information content during the transformation. The following transform tweaks the identity transform to add a date element as the first child of the root.

Here are our changes:

  1. Access the EXSLT extensions for dates and times in Xalan-J by adding the date: namespace.
  2. Exclude the date: namespace from the results by adding it to the exclude-result-prefixes attribute.
  3. Define an XSL date param and initialize it to the current date. Note that the param value specified in the transform may be externally overridden.
  4. Explicitly match on the root element: match=”/*”
    1. Use the name() function to create an element with the same name as the root
    2. Apply the identity template to any attributes of the root element
    3. Add a new date element that contains the value of the date param
    4. Apply the identity template to any children of the root element Producing our new echoWithDate.xsl transform file:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      <?xml version="1.0"?>
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:date="http://exslt.org/dates-and-times"
      exclude-result-prefixes="date"
      version="1.0">

      <xsl:output method="xml"/>

      <xsl:param name="date" select="date:date()"/>
      <xsl:template match="/*" priority="20">
      <xsl:element name="{name()}">
      <xsl:apply-templates select="@*"/>
      <date><xsl:value-of select="$date"/></date>
      <xsl:apply-templates select="node()"/>
      </xsl:element>
      </xsl:template>

      <xsl:template match="@*|node()" priority="10">
      <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
      </xsl:template>
      </xsl:stylesheet>
      We can execute our transform from the command line.
      1
      1 $ cat root.xml
      <Root/>
      2 $ java org.apache.xalan.xslt.Process -IN root.xml -XSL echoWithDate.xsl
      <?xml version="1.0" encoding="UTF-8"?><Root><date>2010-10-02-04:00</date></Root>
      And we can also specify an external value for the date param via the command line.
      1
      3 $ java org.apache.xalan.xslt.Process -IN root.xml -XSL echoWithDate.xsl -PARAM date yesterday
      <?xml version="1.0" encoding="UTF-8"?><Root><date>yesterday</date></Root>
      Now, let’s update the XSL Servlet to add support for parameter values. Previously, we used the path information to specify the transform to run. Here, we extend that to include slash delimited, comma separated parameter name/value pairs: /transform-name/param1-name,param1-value/param2-name,param2-value Here is the updated code from XSL servlet doPost method:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      //
      // Apply named transform to the posted request data
      String path = request.getPathInfo();
      if ( path != null && path.length() > 1 ) {
      String[] pathArgs = path.substring(1).split("/");
      try {
      //
      // 1st path argument specifies the template</b>
      Templates template = templates.get(pathArgs[0]);
      if ( template != null ) {
      Transformer transform = template.newTransformer();
      //
      // Subsequent arguments are param,value pairs
      for ( int i=1 ; i<pathArgs.length ; ++i ) {
      String[] params = pathArgs[i].split(",");
      if ( params.length == 2 && params[0].length() > 0 ) {
      transform.setParameter(params[0],params[1]);
      }
      }
      transform.transform( new StreamSource(in), new StreamResult(out));
      out.close();
      return;
      }
      }
      catch ( TransformerException e ) {
      response.setStatus(response.SC_INTERNAL_SERVER_ERROR);
      return;
      }
      }
      After updating the servlet, adding the transform, and updating our Web Application web.xml; we can see how it works with curl:
      1
      4 $ curl http://localhost:8080/xsl/transform/echoWithDate -d '<root/>'
      <?xml version="1.0" encoding="UTF-8"?><root><date>2010-10-02-04:00</date></root>
      5 $ curl http://localhost:8080/xsl/transform/echoWithDate/date,today -d '<root/>'
      <?xml version="1.0" encoding="UTF-8"?><root><date>today</date></root>
      Here’s the updated XSL Servlet packaged as a war file. Once again, you’ll need to add serializer.jar, xalan.jar and xml-apis.jar from Xalan-J to the web-application WEB-INF/lib directory.

How the Mighty have Fallen

My Connecticut dead-tree edition of the New York Times has no mention of yesterday’s Stanford - Notre Dame football game. Not even a line listing the score in the Scoreboard.

I knew Notre Dame football was slipping off the radar, but I didn’t realize how far the process had progressed.

Using soapUI Project Properties

So far, we haven’t done anything with soapUI that couldn’t be done with curl and a shell script. One of the advantages of soapUI over curl is the built-in support for groovy scripting .

The following example shows the use of a groovy load script to insure that properties are properly initialized. A project load script is specified in the lower load pane of the of the project panel.

soapUI: Project Load Script

Our example simply looks at the project id property and then sets the firstName and lastName properties appropriately. The load script is automatically executed when the project is loaded and can be manually executed by clicking on the green triangle in the upper left corner above the load script pane.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
id = project.getPropertyValue("id")
if ( id == null || id.length() == 0 ) {
project.setPropertyValue("id","")
project.setPropertyValue("firstName","An")
project.setPropertyValue("lastName","Onymous")
}
else if ( id.equalsIgnoreCase("abe") ) {
project.setPropertyValue("firstName","Abraham")
project.setPropertyValue("lastName","Lincoln")
}
else if ( id.equalsIgnoreCase("geo") ) {
project.setPropertyValue("firstName","George")
project.setPropertyValue("lastName","Washington")
}
else {
project.setPropertyValue("id","foo")
project.setPropertyValue("firstName","Foo")
project.setPropertyValue("lastName","Bar")
}
After executing the load script, the properties pane displays the corresponding values. It is also possible to add, define, organize, save [to file] and load [from file] properties directly from this pane.

soapUI: Project Properties

Once the property is defined, then it is easily added to our post data in the form ${#scope#property-name}. There are 3 scopes: Project, TestSuite and TestCase; only Project scope is available at this point. And we can see the property values in the response xml.

soapUI: Using Project Properties

You can download the updated soapUI Project File.

Adding a REST Service to SoapUI

soapUI is a “open source functional testing tool for Web Services”. In this post, we’ll add the XSL Servlet as a soapUI project.

To help you get started, here is the XSL servlet as a java war file. This war requires the addition of serializer.jar, xalan.jar and xml-apis.jar from Xalan-J to the web-application WEB-INF/lib directory to run.

First, create a new project in soapUI. That bring up the dialog shown below. For this example, I’ve named the new service Ideoplex, specified the base url for the service as http://localhost:8080/xsl/transform and requested that we proceed to add a REST resource as our next step.

soapUI: Create New Project

Name the resource “Get environment” and specify an endpoint of environment (this will be appended to the service endpoint defined for our project.

soapUI: New REST Resource

Name the method “Get Environment” and verify that the HTTP Method is “GET”.

soapUI: New REST Method

And execute this request by clicking on the green triangle in the upper left. soapUI will display the response (the environment.xsl transform configured in the java web application) in the right window pane with a response time in the window footer.

soapUI: New REST Request

Repeat the preceding 3 steps to add the corresponding POST request. Be sure to specify POST as the HTTP Method when defining the Method and to add a valid xml file as input to the Request.

soapUI: Add REST Request

Continue and add GET and POST methods for the echo and remove transforms. That should result in a the soapUI project file linked here.

Sep 25: Using soapUI Project Properties

Simple XSL Transforms

Before we deploy our XSL servlet, let’s look at some simple transforms from the command line. First, a simple transform that detects the root node and then outputs the xalan environment. Naturally, this will only work when you’re using xalan .

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xalan="http://xml.apache.org/xalan"
exclude-result-prefixes="xalan">

<xsl:output method="xml"/>

<xsl:template match="/">
<xsl:copy-of select="xalan:checkEnvironment()"/>
</xsl:template>
</xsl:stylesheet>
From the command line:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
1 $ cat input.xml
<?xml version="1.0"?>
<root>
<child remove="attribute">
<grandchild>foo</grandchild>
</child>

<remove>
<alpha/>
</remove>
</root>

2 $ java org.apache.xalan.xslt.Process -IN input.xml -XSL metadata/xsl/environment.xsl
<?xml version="1.0" encoding="UTF-8"?><checkEnvironmentExtension>
<EnvironmentCheck version="$Revision$">
<environment>
<item key="version.DOM.draftlevel">2.0fd</item>
<item key="java.class.path">.;C:\Program Files\Java\jre1.5.0_16\lib\ext\QTJava.zip</item>

<item key="version.JAXP">1.1 or higher</item>
<item key="java.ext.dirs">C:\program files\Java\jdk1.5.0_16\jre\lib\ext</item>

<foundJar desc="apparent.version" name="serializer.jar">serializer.jar present-unknown-version</foundJar>
<foundJar desc="path" name="serializer.jar">C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\serializer.jar</foundJar>

<foundJar desc="path" name="xalan.jar">C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\xalan.jar</foundJar>
<foundJar desc="apparent.version" name="xercesImpl.jar">xercesImpl.jar WARNING.present-unknown-version</foundJar>
<foundJar desc="path" name="xercesImpl.jar">C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\xercesImpl.jar</foundJar>

<foundJar desc="apis.jar-apparent.version" name="xml">xml-apis.jar present-unknown-version</foundJar>
<foundJar desc="apis.jar-path" name="xml">C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\xml-apis.jar</foundJar>

<foundJar desc="apparent.version" name="xsltc.jar">xsltc.jar present-unknown-version</foundJar>
<foundJar desc="path" name="xsltc.jar">C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\xsltc.jar</foundJar>

<item key="version.xerces2">Xerces-J 2.9.0</item>
<item key="version.xerces1">not-present</item>

<item key="version.xalan2_2">Xalan Java 2.7.1</item>
<item key="version.xalan1">not-present</item>

<item key="version.ant">not-present</item>
<item key="java.version">1.5.0_16</item>

<item key="version.DOM">2.0</item>
<item key="version.crimson">not-present</item>

<item key="sun.boot.class.path">C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\serializer.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\xalan.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\xercesImpl.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\xml-apis.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\endorsed\xsltc.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\rt.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\i18n.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\sunrsasign.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\jsse.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\jce.jar;C:\program files\Java\jdk1.5.0_16\jre\lib\charsets.jar;C:\program files\Java\jdk1.5.0_16\jre\classes</item>
<item key="version.SAX">2.0</item>

<item key="version.xalan2x">Xalan Java 2.7.1</item>
</environment>

<status result="OK"/>
</EnvironmentCheck>
</checkEnvironmentExtension>

Next, let’s look at the “identity” transform. There is a single template directive that matches on attributes or elements (@* and node() respectively). For each attribute or element entity, we copy the current attribute or element and then we recursively apply the template to any children of the current element.

lang: xml
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>

<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
From the command line:
1
3 $ java org.apache.xalan.xslt.Process -IN input.xml -XSL metadata/xsl/echo.xsl
<?xml version="1.0"?>
<root>
  <child remove="attribute">
    <grandchild>foo</grandchild>
  </child>
  <remove>
    <alpha/>
  </remove>
</root>
Note that this simple transform only copies attributes and elements. Other entities, such as comments, will be ignored. And finally, a transform that removes any element named “remove” along with its children. We start with the identity transform, adding a priority attribute to the first template directive. We add a second template directive that only matches elements named “remove” with a higher priority value.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>

<xsl:template match="@*|node()" priority="10">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>

<xsl:template match="remove" priority="20">
</xsl:template>

</xsl:stylesheet>
From the command line:
1
4 $ java org.apache.xalan.xslt.Process -IN input.xml -XSL metadata/xsl/remove.xsl
<?xml version="1.0" encoding="UTF-8"?><root>
  <child remove="attribute">
    <grandchild>foo</grandchild>
  </child>

</root>
If you change the match attribute from “remove” to “@remove”, then attributes named remove will be eliminated rather than elements. And using a match attribute of “@remove|remove” will eliminate both attributes and elements named “remove”.

Sep 12: Adding a REST Service to SoapUI

Oct 2: Using XSL Param

A Simple XSL Servlet

If you only have a hammer, you tend to see every problem as a nail.

Abraham Maslow

I try to restrain myself, but one of my more common workplace rants is:

If you start with xml and you end up with xml, then why aren’t you using xsl in between?

My colleagues commonly unmarshal xml to an object, manipulate the object, and marshal the object back to xml. The right choice when there are relatively complex manipulations in the middle. But xsl is a much better choice for simple manipulations:

  • Easier to review
  • Easier to test
  • Easier to modify
  • Easier to track change

Here’s a simple servlet that applies xsl transforms to posted request xml. First, the init method that reads in xsl tranform files located in /WEB-INF/xsl and stores the corresponding Templates objects in a HashMap (Templates are thread-safe).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void init(ServletConfig config)
throws ServletException
{

super.init(config);

//
// Force use of xalan for environment.xsl: xalan:checkEnvironment()
TransformerFactory factory = new org.apache.xalan.processor.TransformerFactoryImpl();

templates = new HashMap<String,Templates>();
String list = config.getInitParameter("transforms");
if ( list != null ) {
String[] names = list.split("\\s*,\\s*");
for ( String name : names ) {
if ( name.length() > 0 ) {
try {
InputStream in = getTemplateInputStream(getServletContext(),name);
if ( in != null ) {
Templates template = factory.newTemplates(new StreamSource(in));
templates.put( name,template );
}
}
catch (Exception e) { ; }
}
}
}
}

Next, a get method that returns the xsl transform whose name matches the request path information.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void doGet(HttpServletRequest request,
HttpServletResponse response)

throws java.io.IOException,
ServletException
{

//
// Return the xsl transform if specified in the path
String path = request.getPathInfo();
if ( path != null && path.length() > 1 ) {
path = path.substring(1);
if ( templates.get(path) != null ) {
response.setContentType("application/xml;charset=utf-8");
InputStream in = getTemplateInputStream(getServletContext(),path);
PrintWriter out = response.getWriter();
int ch;
while ( (ch=in.read()) > -1) {
out.write(ch);
}
return;
}
}

//
// Otherwise list loaded transforms
response.setContentType("application/text;charset=utf-8");
PrintWriter out = response.getWriter();
Object[] keys = templates.keySet().toArray();
out.println("Supported transforms are:");
for ( Object key : keys ) {
out.print("\t");
out.println(key.toString());
}
}
And finally, a post method that applies the xsl transform identified in the path information to the posted xml request body:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void doPost(HttpServletRequest request,
HttpServletResponse response)

throws java.io.IOException,
ServletException
{

InputStream in = request.getInputStream();
response.setContentType("application/xml;charset=utf-8");
PrintWriter out = response.getWriter();

//
// Apply named transform to the posted request data
String path = request.getPathInfo();
if ( path != null && path.length() > 1 ) {
try {
Templates template = templates.get(path.substring(1));
if ( template != null ) {
Transformer transform = template.newTransformer();
transform.transform( new StreamSource(in), new StreamResult(out));
out.close();
return;
}
}
catch ( TransformerException e ) {
response.setStatus(response.SC_INTERNAL_SERVER_ERROR);
return;
}
}

response.setStatus(response.SC_FORBIDDEN);
}
In the next installment, we’ll add some xsl transforms and take our servlet out for a spin.