Example 10-11. Skeleton of XSP tag library
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:javaxml2="http://www.oreilly.com/javaxml2"
>
<xsl:template match="xsp:page">
<xsp:page>
<xsl:copy>
<xsl:apply-templates select="@*"/>
</xsl:copy>
<xsl:apply-templates/>
</xsp:page>
</xsl:template>
<xsl:template match="@*|*|text()|processing-instruction( )">
<xsl:copy>
<xsl:apply-templates
select="@*|*|text()|processing-instruction( )"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
By matching the xsp:page tag, it's possible
to ensure that all elements are matched and handled within this
stylesheet; this is referred to as a logicsheet
in XSP parlance. Now add Java methods for the templates within this
logicsheet to call:
<xsl:template match="xsp:page">
<xsp:page>
<xsl:copy>
<xsl:apply-templates select="@*"/>
</xsl:copy>
<xsp:structure>
<xsp:include>java.util.Date</xsp:include>
<xsp:include>java.text.SimpleDateFormat</xsp:include>
</xsp:structure>
<xsp:logic>
private static String getDraftDate( ) {
return (new SimpleDateFormat("MM/dd/yyyy"))
.format(new Date( ));
}
private static String getTitle(int chapterNum,
String chapterTitle) {
return chapterNum + ". " + chapterTitle;
}
</xsp:logic>
<xsl:apply-templates/>
</xsp:page>
</xsl:template>
Several new XSP elements are introduced here. First,
xsp:structure is used to surround several
xsp:include statements. These work just like their
Java counterpart, import, making the specified
Java classes available for use by their unqualified names (rather
than the complete packaged names). Once these are available, the
logicsheet defines and implements two methods: one that creates a
chapter title from the chapter number and textual title, and one that
returns the current date as a formatted String.
These methods are available to any elements within this logicsheet.
Now define the element that specifies when an XSP result should
replace an XML element. The javaxml2-associated
namespace is already declared in the root element, so it can be used
as the namespace for the new tag library elements. Add the following
template into your logicsheet:
<!-- Create formatted title -->
<xsl:template match="javaxml2:draftTitle">
<xsp:expr>getTitle(<xsl:value-of select="@chapterNum" />,
"<xsl:value-of select="@chapterTitle" />")
</xsp:expr> (<xsp:expr>getDraftDate( )</xsp:expr>)
</xsl:template>
<xsl:template match="@*|*|text()|processing-instruction( )">
<xsl:copy>
<xsl:apply-templates
select="@*|*|text()|processing-instruction( )"/>
</xsl:copy>
</xsl:template>
When a document with this tag library uses the element
javaxml2:draftTitle (or just
draftTitle if the default namespace is mapped to
http://www.oreilly.com/javaxml2),
the result of the method getTitle( ) is prepended
to the value of the getDraftDate( ) method. The
javaxml2:draftTitle element also expects two
attributes to be declared: the chapter number and the textual title
of the chapter. Signify to the XSP processor that you are calling a
defined method by enclosing the method call within a set of
<xsp:expr> tags. To indicate that the second
argument (the chapter title) is a String, it
should be enclosed within quotes. Since the chapter number should be
treated as an int, it is left without quotation
marks.
Once you have completed the XSP logicsheet (available online at the
book's web site as well), you need to make it accessible to
Cocoon. This can be done one of two ways. The first is to specify the
location of the file as a URI, which allows the servlet engine (and
therefore Cocoon) to locate the logicsheet. For example, to add the
XSP logicsheet to Cocoon's set of resources through its URI,
you could add the following lines to the cocoon.properties file on a Unix-based
system:
# Set the libraries associated with the given namespace.
# Use the syntax:
# processor.xsp.logicsheet.<namespace-tag>.<language> = URL to file
# where "URL to file" is usually starting with file:// if you locate
# your custom library in your file system.
processor.xsp.logicsheet.context.java =resource://org/apache/cocoon/processor/
xsp/library/java/context.xsl
processor.xsp.logicsheet.cookie.java =resource://org/apache/cocoon/processor/
xsp/library/java/cookie.xsl
processor.xsp.logicsheet.global.java =resource://org/apache/cocoon/processor/
xsp/library/java/global.xsl
processor.xsp.logicsheet.request.java =resource://org/apache/cocoon/processor/
xsp/library/java/request.xsl
processor.xsp.logicsheet.response.java =resource://org/apache/cocoon/processor/
xsp/library/java/response.xsl
processor.xsp.logicsheet.session.java =resource://org/apache/cocoon/processor/
xsp/library/java/session.xsl
processor.xsp.logicsheet.util.java =
resource://org/apache/cocoon/processor/xsp/library/java/util.xsl
processor.xsp.logicsheet.sql.java =
resource://org/apache/cocoon/processor/xsp/library/sql/sql.xsl
processor.xsp.logicsheet.esql.java =
resource://org/apache/cocoon/processor/xsp/library/sql/esql.xsl
processor.xsp.logicsheet.fp.java =
resource://org/apache/cocoon/processor/xsp/library/fp/fp.xsl
processor.xsp.library.JavaXML.java =
file:///usr/local/jakarta-tomcat/webapps/cocoon/XSL/JavaXML.xsp.xsl
For Windows systems, this would be:
# Set the libraries associated with the given namespace.
# Use the syntax:
# processor.xsp.logicsheet.<namespace-tag>.<language> = URL to file
# where "URL to file" is usually starting with file:// if you locate
# your custom library in your file system.
processor.xsp.logicsheet.context.java =resource://org/apache/cocoon/processor/
xsp/library/java/context.xsl
processor.xsp.logicsheet.cookie.java =resource://org/apache/cocoon/processor/
xsp/library/java/cookie.xsl
processor.xsp.logicsheet.global.java =resource://org/apache/cocoon/processor/
xsp/library/java/global.xsl
processor.xsp.logicsheet.request.java =resource://org/apache/cocoon/processor/
xsp/library/java/request.xsl
processor.xsp.logicsheet.response.java =resource://org/apache/cocoon/processor/
xsp/library/java/response.xsl
processor.xsp.logicsheet.session.java =resource://org/apache/cocoon/processor/
xsp/library/java/session.xsl
processor.xsp.logicsheet.util.java =
resource://org/apache/cocoon/processor/xsp/library/java/util.xsl
processor.xsp.logicsheet.sql.java =
resource://org/apache/cocoon/processor/xsp/library/sql/sql.xsl
processor.xsp.logicsheet.esql.java =
resource://org/apache/cocoon/processor/xsp/library/sql/esql.xsl
processor.xsp.logicsheet.fp.java =
resource://org/apache/cocoon/processor/xsp/library/fp/fp.xsl
processor.xsp.library.javaxml2.java =
file:///C:/java/jakarta-tomcat/webapps/cocoon/XSL/JavaXML.xsp.xsl
While this is handy for testing, it is not a very good solution for
uncoupling your logicsheets from the servlet engine, and also adds
quite a bit of maintenance overhead when adding new logicsheets: a
new line would have to be added to the cocoon.properties file for new logicsheets to
be available. Allowing specification of a resource in the servlet
engine's classpath is an alternative method for loading
logicsheets. This lets you add custom logicsheets to a jar file, and that jar file to the servlet engine classpath
(which in Tomcat simply means adding that archive to the lib/ directory!). In addition, new
logicsheets can be put within the jar file, providing a central location for
storing your custom XSP logicsheets. From the XSL/ subdirectory in your web server's
document root, perform the following command to create a jar file that contains your logicsheet:
jar cvf logicsheets.jar JavaXML.xsp.xsl
Move the created logicsheets.jar
archive into your TOMCAT_HOME/lib/ directory
with the other Cocoon libraries. That will ensure that Tomcat loads
the library on startup. With your logicsheet available, you can now
let Cocoon know where to look for
javaxml2-associated namespace references within
XSP pages. Edit the cocoon.properties file; locate the section
that lists the various Cocoon XSP resources, and add the new
logicsheet reference:
# Set the libraries associated with the given namespace.
# Use the syntax:
# processor.xsp.logicsheet.<namespace-tag>.<language> = URL to file
# where "URL to file" is usually starting with file:// if you locate
# your custom library in your file system.
processor.xsp.logicsheet.context.java =resource://org/apache/cocoon/processor/
xsp/library/java/context.xsl
processor.xsp.logicsheet.cookie.java =resource://org/apache/cocoon/processor/
xsp/library/java/cookie.xsl
processor.xsp.logicsheet.global.java =resource://org/apache/cocoon/processor/
xsp/library/java/global.xsl
processor.xsp.logicsheet.request.java =resource://org/apache/cocoon/processor/
xsp/library/java/request.xsl
processor.xsp.logicsheet.response.java =resource://org/apache/cocoon/processor/
xsp/library/java/response.xsl
processor.xsp.logicsheet.session.java =resource://org/apache/cocoon/processor/
xsp/library/java/session.xsl
processor.xsp.logicsheet.util.java =
resource://org/apache/cocoon/processor/xsp/library/java/util.xsl
processor.xsp.logicsheet.sql.java =
resource://org/apache/cocoon/processor/xsp/library/sql/sql.xsl
processor.xsp.logicsheet.esql.java =
resource://org/apache/cocoon/processor/xsp/library/sql/esql.xsl
processor.xsp.logicsheet.fp.java =
resource://org/apache/cocoon/processor/xsp/library/fp/fp.xsl
processor.xsp.logicsheet.javaxml2.java = resource://JavaXML.xsp.xsl
Because the logicsheet is not nested within any subdirectories in the
logicsheets.jar file, you can
simply use the name of the logicsheet as its resource path. Finally,
restart your servlet engine (this also ensures that Tomcat auto-loads
the new library). This will reload the cocoon.properties file, and the logicsheet
will be available for use. As the Cocoon engine is used to handle
requests, any XSP page that declares that it uses the javaxml2
namespace will have access to the logicsheet specified as the
javaxml2 library. So the XSP page needs to add a namespace
declaration for the javaxml2 namespace:
<?xml version="1.0"?>
<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>
<?xml-stylesheet href="XSL/JavaXML.fo.xsl" type="text/xsl"?>
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:javaxml2="http://www.oreilly.com/javaxml2"
>
<book>
<!-- Book content -->
</book>
</xsp:page>
With the tag library now available for use, you can finally add in
the javaxml2:draftTitle element to your XML
document, chapterTen.xml:
<contents>
<chapter title="Web Publishing Frameworks" number="10">
<javaxml2:draftTitle chapterNum="10"
chapterTitle="Web Publishing Framework" />
...
Replace the hardcoded chapter title with the element defined in the
XSP tag library with the following change to your JavaXML.fo.xsl stylesheet:
<xsl:template match="chapter">
<fo:block font-size="24pt"
text-align-last="center"
space-before.optimum="24pt">
<!--
<xsl:value-of select="@number" />.
<xsl:value-of select="@title" />
-->
<xsl:apply-templates/>
</fo:block>
</xsl:template>