home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Practical mod_perlPractical mod_perlSearch this book

E.3. Dynamic Content

AxKit has a flexible tool called eXtensible Server Pages (XSP) for creating XML from various data sources such as relational databases, cookies, and form parameters. This technology was originally invented by the Apache Cocoon team, and AxKit shares their syntax. This allows easier migration of projects to and from Cocoon. (Cocoon allows you to embed Java code in your XSP, similar to how AxKit allows you to embed Perl code.)

XSP is an XML-based syntax that uses namespaces to provide extensibility. In many ways, this is like the Cold Fusion model of using tags to provide dynamic functionality. One of the advantages of using XSP is that it is impossible to generate invalid XML, which makes it ideal for use in an XML framework such as AxKit. Another is that the tags can hide complex functionality, allowing the XSP tags to be added by designers and freeing programmers to perform more complex and more cost-effective tasks.

The XSP framework allows you to design new tags, or use ones provided already by others on CPAN. These extra tags are called taglibs. By using taglibs instead of embedding Perl code in your XSP page, you can further build on AxKit's separation of content from presentation by separating out logic too. And creating new taglibs is almost trivial using AxKit's TagLibHelper module, which hides all the details for you.

In the examples below, we are going to show some code that embeds Perl code in the XSP pages. This is not a recommended practice, due to the ease with which you can extract functionality into tag libraries. However, it is more obvious to Perl programmers what is going on this way and provides a good introduction to the technology.

E.3.1. Handling Form Parameters

The AxKit::XSP::Param taglib allows you to easily read form and query string parameters within an XSP page. The following example shows how a page can submit back to itself. To allow this to work, add the following to your httpd.conf file:

AxAddXSPTaglib AxKit::XSP::Param

The XSP page is shown in Example E-3.

Example E-3. paramtaglib.xsp

<xsp:page
 xmlns:xsp="http://apache.org/xsp/core/v1"
 xmlns:param="http://axkit.org/NS/xsp/param/v1"
 language="Perl"
>
<page>
  <xsp:logic>
  if (<param:name/>) {
    <xsp:content>
     Your name is: <param:name/>
    </xsp:content>
  }
  else {
    <xsp:content>
      <form>
        Enter your name: <input type="text" name="name" />
        <input type="submit"/>
      </form>
    </xsp:content>
  }
  </xsp:logic>
</page>
</xsp:page>

The most significant thing about this example is how we freely mix XML tags with our Perl code, and the XSP processor figures out the right thing to do depending on the context. The only requirement is that the XSP page itself must be valid XML. That is, the following would generate an error:

<xsp:logic>
my $page = <param:page/>;
if ($page < 3) { # ERROR: less-than is a reserved character in XML
 ...
}
</xsp:logic>

We need to convert this to valid XML before XSP can handle it. There are a number of ways to do so. The simplest is just to reverse the expression to if (3 > $page), because the greater-than sign is valid within an XML text section. Another way is to encode the less-than sign as &lt;, which will be familiar to HTML authors.

The other thing to notice is the <xsp:logic> and <xsp:content> tags. The former defines a section of Perl code, while the latter allows you to go back to processing the contents as XML output. Also note that the <xsp:content> tag is not always needed. Because the XSP engine inherently understands XML, you can omit the <xsp:content> tag when the immediate child would be an element, rather than text. For example, the following example requires the <xsp:content> tag:

<xsp:logic>
if (<param:name/>) {
  # xsp:content needed
  <xsp:content>
  Your name is: <param:name/>
  </xsp:content>
}
</xsp:logic>

But if you rewrote it like this, it wouldn't, because of the surrounding non-XSP tag:

<xsp:logic>
if (<param:name/>) {
  # no xsp:content tag needed
  <p>Your name is: <param:name/></p>
}
</xsp:logic>

Note that the initial example, when processed by only the XSP engine, will output the following XML:

<page>
  <form>
    Enter your name: <input type="text" name="name" />
    <input type="submit"/>
  </form>
</page>

This needs to be processed with XSLT or XPathScript to be reasonably viewable in a browser. However, the point is that you can reuse the above page as either HTML or WML just by applying different stylesheets.

E.3.6. Executing SQL

Perhaps the most interesting taglib of all is the ESQL taglib, which allows you to execute SQL queries against a DBI-compatible database and provides access to the column return values as strings, scalars, numbers, dates, or even as XML. (Returning XML requires the utilities taglib.) Like the sendmail taglib, the ESQL taglib throws exceptions when an error occurs.

One point of interest about the ESQL taglib is that it is a direct copy of the Cocoon ESQL taglib. There are only a few minor differences between the two, such as how columns of different types are returned and how errors are trapped.[65] Having nearly identical taglibs helps you to port projects to or from Cocoon. As with all the other taglibs, ESQL requires the addition of the following to your httpd.conf file:

[65]In Cocoon there are ESQL tags for trapping errors, whereas AxKit uses exceptions.

AxAddXSPTaglib AxKit::XSP::ESQL

Example E-7 uses ESQL to read data from an address-book table. This page demonstrates that it is possible to reuse the same code for both our list of addresses and viewing a single address in detail.

Example E-7. esqltaglib.xsp

<xsp:page
 language="Perl"
 xmlns:xsp="http://apache.org/xsp/core/v1"
 xmlns:esql="http://apache.org/xsp/SQL/v2"
 xmlns:except="http://axkit.org/NS/xsp/exception/v1"
 xmlns:param="http://axkit.org/NS/xsp/param/v1"
 indent-result="no"
>
<addresses>
 <esql:connection>
  <esql:driver>Pg</esql:driver>
  <esql:dburl>dbname=phonebook</esql:dburl>
  <esql:username>postgres</esql:username>
  <esql:password></esql:password>
  <except:try>
  <esql:execute-query>
   <xsp:logic>
   if (<param:address_id/>) {
    <esql:query>
     SELECT * FROM address WHERE id =
     <esql:parameter><param:address_id/></esql:parameter>
    </esql:query>
   }
   else {
    <esql:query>
     SELECT * FROM address
    </esql:query>
   }
   </xsp:logic>
   <esql:results>
    <esql:row-results>
     <address>
      <esql:get-columns/>
     </address>
    </esql:row-results>
   </esql:results>
  </esql:execute-query>

  <except:catch>
   Error Occured: <except:message/>
  </except:catch>
  </except:try>
 </esql:connection>
</addresses>
</xsp:page>

The result of running the above through the XSP processor is:

<addresses>
 <address>
  <id>2</id>
  <last_name>Sergeant</last_name>
  <first_name>Matt</first_name>
  <title>Mr</title>
  <company>AxKit.com Ltd</company>
  <email>matt@axkit.com</email>
  <classification_id>1</classification_id>
 </address>
</addresses>


Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.